diff --git a/.gitignore b/.gitignore index 97ba6b79834c6d..f0ab7a367fb22a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ # command after changing this file, to see if there are # any tracked files which get ignored after the change. # +busybox/_install/* +linux*.si4project/ # Normal rules (sorted alphabetically) # .* diff --git a/Documentation/ABI/stable/sysfs-bus-vmbus b/Documentation/ABI/stable/sysfs-bus-vmbus index 0c9d9dcd2151d1..3eaffbb2d46893 100644 --- a/Documentation/ABI/stable/sysfs-bus-vmbus +++ b/Documentation/ABI/stable/sysfs-bus-vmbus @@ -1,25 +1,25 @@ -What: /sys/bus/vmbus/devices/vmbus_*/id +What: /sys/bus/vmbus/devices//id Date: Jul 2009 KernelVersion: 2.6.31 Contact: K. Y. Srinivasan Description: The VMBus child_relid of the device's primary channel Users: tools/hv/lsvmbus -What: /sys/bus/vmbus/devices/vmbus_*/class_id +What: /sys/bus/vmbus/devices//class_id Date: Jul 2009 KernelVersion: 2.6.31 Contact: K. Y. Srinivasan Description: The VMBus interface type GUID of the device Users: tools/hv/lsvmbus -What: /sys/bus/vmbus/devices/vmbus_*/device_id +What: /sys/bus/vmbus/devices//device_id Date: Jul 2009 KernelVersion: 2.6.31 Contact: K. Y. Srinivasan Description: The VMBus interface instance GUID of the device Users: tools/hv/lsvmbus -What: /sys/bus/vmbus/devices/vmbus_*/channel_vp_mapping +What: /sys/bus/vmbus/devices//channel_vp_mapping Date: Jul 2015 KernelVersion: 4.2.0 Contact: K. Y. Srinivasan @@ -28,112 +28,112 @@ Description: The mapping of which primary/sub channels are bound to which Format: Users: tools/hv/lsvmbus -What: /sys/bus/vmbus/devices/vmbus_*/device +What: /sys/bus/vmbus/devices//device Date: Dec. 2015 KernelVersion: 4.5 Contact: K. Y. Srinivasan Description: The 16 bit device ID of the device Users: tools/hv/lsvmbus and user level RDMA libraries -What: /sys/bus/vmbus/devices/vmbus_*/vendor +What: /sys/bus/vmbus/devices//vendor Date: Dec. 2015 KernelVersion: 4.5 Contact: K. Y. Srinivasan Description: The 16 bit vendor ID of the device Users: tools/hv/lsvmbus and user level RDMA libraries -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN +What: /sys/bus/vmbus/devices//channels/ Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Directory for per-channel information NN is the VMBUS relid associtated with the channel. -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/cpu +What: /sys/bus/vmbus/devices//channels//cpu Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: VCPU (sub)channel is affinitized to Users: tools/hv/lsvmbus and other debugging tools -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/cpu +What: /sys/bus/vmbus/devices//channels//cpu Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: VCPU (sub)channel is affinitized to Users: tools/hv/lsvmbus and other debugging tools -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/in_mask +What: /sys/bus/vmbus/devices//channels//in_mask Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Host to guest channel interrupt mask Users: Debugging tools -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/latency +What: /sys/bus/vmbus/devices//channels//latency Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Channel signaling latency Users: Debugging tools -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/out_mask +What: /sys/bus/vmbus/devices//channels//out_mask Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Guest to host channel interrupt mask Users: Debugging tools -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/pending +What: /sys/bus/vmbus/devices//channels//pending Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Channel interrupt pending state Users: Debugging tools -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/read_avail +What: /sys/bus/vmbus/devices//channels//read_avail Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Bytes available to read Users: Debugging tools -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/write_avail +What: /sys/bus/vmbus/devices//channels//write_avail Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Bytes available to write Users: Debugging tools -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/events +What: /sys/bus/vmbus/devices//channels//events Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Number of times we have signaled the host Users: Debugging tools -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/interrupts +What: /sys/bus/vmbus/devices//channels//interrupts Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Number of times we have taken an interrupt (incoming) Users: Debugging tools -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/subchannel_id +What: /sys/bus/vmbus/devices//channels//subchannel_id Date: January. 2018 KernelVersion: 4.16 Contact: Stephen Hemminger Description: Subchannel ID associated with VMBUS channel Users: Debugging tools and userspace drivers -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/monitor_id +What: /sys/bus/vmbus/devices//channels//monitor_id Date: January. 2018 KernelVersion: 4.16 Contact: Stephen Hemminger Description: Monitor bit associated with channel Users: Debugging tools and userspace drivers -What: /sys/bus/vmbus/devices/vmbus_*/channels/NN/ring +What: /sys/bus/vmbus/devices//channels//ring Date: January. 2018 KernelVersion: 4.16 Contact: Stephen Hemminger diff --git a/Documentation/ABI/testing/sysfs-class-cxl b/Documentation/ABI/testing/sysfs-class-cxl index 640f65e79ef1c0..8e69345c37ccd9 100644 --- a/Documentation/ABI/testing/sysfs-class-cxl +++ b/Documentation/ABI/testing/sysfs-class-cxl @@ -244,3 +244,11 @@ Description: read only Returns 1 if the psl timebase register is synchronized with the core timebase register, 0 otherwise. Users: https://github.com/ibm-capi/libcxl + +What: /sys/class/cxl//tunneled_ops_supported +Date: May 2018 +Contact: linuxppc-dev@lists.ozlabs.org +Description: read only + Returns 1 if tunneled operations are supported in capi mode, + 0 otherwise. +Users: https://github.com/ibm-capi/libcxl diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index 025b7cf3768dc6..bd4975e132d343 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -478,6 +478,7 @@ What: /sys/devices/system/cpu/vulnerabilities /sys/devices/system/cpu/vulnerabilities/meltdown /sys/devices/system/cpu/vulnerabilities/spectre_v1 /sys/devices/system/cpu/vulnerabilities/spectre_v2 + /sys/devices/system/cpu/vulnerabilities/spec_store_bypass Date: January 2018 Contact: Linux kernel mailing list Description: Information about CPU vulnerabilities diff --git a/Documentation/PCI/2010-real estate regulation.docx b/Documentation/PCI/2010-real estate regulation.docx new file mode 100644 index 00000000000000..b45aef85d197e3 Binary files /dev/null and b/Documentation/PCI/2010-real estate regulation.docx differ diff --git a/Documentation/PCI/Linux PCIE.xmind b/Documentation/PCI/Linux PCIE.xmind new file mode 100644 index 00000000000000..f4504779a07b78 Binary files /dev/null and b/Documentation/PCI/Linux PCIE.xmind differ diff --git a/Documentation/PCI/pcie-port-bus-driver-support-for-inux.pdf b/Documentation/PCI/pcie-port-bus-driver-support-for-inux.pdf new file mode 100644 index 00000000000000..2c3b9d7f06f256 Binary files /dev/null and b/Documentation/PCI/pcie-port-bus-driver-support-for-inux.pdf differ diff --git a/Documentation/acpi/acpi_6_2.pdf b/Documentation/acpi/acpi_6_2.pdf new file mode 100644 index 00000000000000..c19b85448ecbbf Binary files /dev/null and b/Documentation/acpi/acpi_6_2.pdf differ diff --git a/Documentation/acpi/acpi_linux_2005.pdf b/Documentation/acpi/acpi_linux_2005.pdf new file mode 100644 index 00000000000000..9e841d5676e6d7 Binary files /dev/null and b/Documentation/acpi/acpi_linux_2005.pdf differ diff --git a/Documentation/acpi/asl2.0overview.pdf b/Documentation/acpi/asl2.0overview.pdf new file mode 100644 index 00000000000000..fd544c23d3b87b Binary files /dev/null and b/Documentation/acpi/asl2.0overview.pdf differ diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 11fc28ecdb6d9f..f2040d46f0956b 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2680,6 +2680,9 @@ allow data leaks with this option, which is equivalent to spectre_v2=off. + nospec_store_bypass_disable + [HW] Disable all mitigations for the Speculative Store Bypass vulnerability + noxsave [BUGS=X86] Disables x86 extended register state save and restore using xsave. The kernel will fallback to enabling legacy floating-point and sse state. @@ -4025,6 +4028,48 @@ Not specifying this option is equivalent to spectre_v2=auto. + spec_store_bypass_disable= + [HW] Control Speculative Store Bypass (SSB) Disable mitigation + (Speculative Store Bypass vulnerability) + + Certain CPUs are vulnerable to an exploit against a + a common industry wide performance optimization known + as "Speculative Store Bypass" in which recent stores + to the same memory location may not be observed by + later loads during speculative execution. The idea + is that such stores are unlikely and that they can + be detected prior to instruction retirement at the + end of a particular speculation execution window. + + In vulnerable processors, the speculatively forwarded + store can be used in a cache side channel attack, for + example to read memory to which the attacker does not + directly have access (e.g. inside sandboxed code). + + This parameter controls whether the Speculative Store + Bypass optimization is used. + + on - Unconditionally disable Speculative Store Bypass + off - Unconditionally enable Speculative Store Bypass + auto - Kernel detects whether the CPU model contains an + implementation of Speculative Store Bypass and + picks the most appropriate mitigation. If the + CPU is not vulnerable, "off" is selected. If the + CPU is vulnerable the default mitigation is + architecture and Kconfig dependent. See below. + prctl - Control Speculative Store Bypass per thread + via prctl. Speculative Store Bypass is enabled + for a process by default. The state of the control + is inherited on fork. + seccomp - Same as "prctl" above, but all seccomp threads + will disable SSB unless they explicitly opt out. + + Not specifying this option is equivalent to + spec_store_bypass_disable=auto. + + Default mitigations: + X86: If CONFIG_SECCOMP=y "seccomp", otherwise "prctl" + spia_io_base= [HW,MTD] spia_fio_base= spia_pedr= diff --git a/Documentation/admin-guide/pm/intel_pstate.rst b/Documentation/admin-guide/pm/intel_pstate.rst index d2b6fda3d67b97..ab2fe0eda1d7c3 100644 --- a/Documentation/admin-guide/pm/intel_pstate.rst +++ b/Documentation/admin-guide/pm/intel_pstate.rst @@ -145,7 +145,7 @@ feature enabled.] In this mode ``intel_pstate`` registers utilization update callbacks with the CPU scheduler in order to run a P-state selection algorithm, either -``powersave`` or ``performance``, depending on the ``scaling_cur_freq`` policy +``powersave`` or ``performance``, depending on the ``scaling_governor`` policy setting in ``sysfs``. The current CPU frequency information to be made available from the ``scaling_cur_freq`` policy attribute in ``sysfs`` is periodically updated by those utilization update callbacks too. diff --git a/Documentation/admin-guide/pm/sleep-states.rst b/Documentation/admin-guide/pm/sleep-states.rst index 1e5c0f00cb2fee..dbf5acd49f350d 100644 --- a/Documentation/admin-guide/pm/sleep-states.rst +++ b/Documentation/admin-guide/pm/sleep-states.rst @@ -15,7 +15,7 @@ Sleep States That Can Be Supported ================================== Depending on its configuration and the capabilities of the platform it runs on, -the Linux kernel can support up to four system sleep states, includig +the Linux kernel can support up to four system sleep states, including hibernation and up to three variants of system suspend. The sleep states that can be supported by the kernel are listed below. diff --git a/Documentation/device-mapper/thin-provisioning.txt b/Documentation/device-mapper/thin-provisioning.txt index 4bcd4b7f79f943..3d01948ea0611f 100644 --- a/Documentation/device-mapper/thin-provisioning.txt +++ b/Documentation/device-mapper/thin-provisioning.txt @@ -264,7 +264,10 @@ i) Constructor data device, but just remove the mapping. read_only: Don't allow any changes to be made to the pool - metadata. + metadata. This mode is only available after the + thin-pool has been created and first used in full + read/write mode. It cannot be specified on initial + thin-pool creation. error_if_no_space: Error IOs, instead of queueing, if no space. diff --git a/Documentation/devicetree/bindings/net/can/rcar_canfd.txt b/Documentation/devicetree/bindings/net/can/rcar_canfd.txt index 93c3a6ae32f995..ac71daa4619505 100644 --- a/Documentation/devicetree/bindings/net/can/rcar_canfd.txt +++ b/Documentation/devicetree/bindings/net/can/rcar_canfd.txt @@ -5,7 +5,9 @@ Required properties: - compatible: Must contain one or more of the following: - "renesas,rcar-gen3-canfd" for R-Car Gen3 compatible controller. - "renesas,r8a7795-canfd" for R8A7795 (R-Car H3) compatible controller. - - "renesas,r8a7796-canfd" for R8A7796 (R-Car M3) compatible controller. + - "renesas,r8a7796-canfd" for R8A7796 (R-Car M3-W) compatible controller. + - "renesas,r8a77970-canfd" for R8A77970 (R-Car V3M) compatible controller. + - "renesas,r8a77980-canfd" for R8A77980 (R-Car V3H) compatible controller. When compatible with the generic version, nodes must list the SoC-specific version corresponding to the platform first, followed by the diff --git a/Documentation/devicetree/bindings/net/dsa/b53.txt b/Documentation/devicetree/bindings/net/dsa/b53.txt index 8acf51a4dfa8a5..47a6a7fe0b8643 100644 --- a/Documentation/devicetree/bindings/net/dsa/b53.txt +++ b/Documentation/devicetree/bindings/net/dsa/b53.txt @@ -10,6 +10,7 @@ Required properties: "brcm,bcm53128" "brcm,bcm5365" "brcm,bcm5395" + "brcm,bcm5389" "brcm,bcm5397" "brcm,bcm5398" diff --git a/Documentation/devicetree/bindings/net/marvell-pp2.txt b/Documentation/devicetree/bindings/net/marvell-pp2.txt index 1814fa13f6ab80..fc019df0d8638d 100644 --- a/Documentation/devicetree/bindings/net/marvell-pp2.txt +++ b/Documentation/devicetree/bindings/net/marvell-pp2.txt @@ -21,9 +21,10 @@ Required properties: - main controller clock (for both armada-375-pp2 and armada-7k-pp2) - GOP clock (for both armada-375-pp2 and armada-7k-pp2) - MG clock (only for armada-7k-pp2) + - MG Core clock (only for armada-7k-pp2) - AXI clock (only for armada-7k-pp2) -- clock-names: names of used clocks, must be "pp_clk", "gop_clk", "mg_clk" - and "axi_clk" (the 2 latter only for armada-7k-pp2). +- clock-names: names of used clocks, must be "pp_clk", "gop_clk", "mg_clk", + "mg_core_clk" and "axi_clk" (the 3 latter only for armada-7k-pp2). The ethernet ports are represented by subnodes. At least one port is required. @@ -80,8 +81,8 @@ cpm_ethernet: ethernet@0 { compatible = "marvell,armada-7k-pp22"; reg = <0x0 0x100000>, <0x129000 0xb000>; clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>, - <&cpm_syscon0 1 5>, <&cpm_syscon0 1 18>; - clock-names = "pp_clk", "gop_clk", "gp_clk", "axi_clk"; + <&cpm_syscon0 1 5>, <&cpm_syscon0 1 6>, <&cpm_syscon0 1 18>; + clock-names = "pp_clk", "gop_clk", "mg_clk", "mg_core_clk", "axi_clk"; eth0: eth0 { interrupts = , diff --git a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt index 42a248301615d9..e22d8cfea68743 100644 --- a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt +++ b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt @@ -57,6 +57,13 @@ KSZ9031: - txd2-skew-ps : Skew control of TX data 2 pad - txd3-skew-ps : Skew control of TX data 3 pad + - micrel,force-master: + Boolean, force phy to master mode. Only set this option if the phy + reference clock provided at CLK125_NDO pin is used as MAC reference + clock because the clock jitter in slave mode is to high (errata#2). + Attention: The link partner must be configurable as slave otherwise + no link will be established. + Examples: mdio { diff --git a/Documentation/i2c/busses/i2c-ocores b/Documentation/i2c/busses/i2c-ocores index c269aaa2f26a1a..9e1dfe7553ad8e 100644 --- a/Documentation/i2c/busses/i2c-ocores +++ b/Documentation/i2c/busses/i2c-ocores @@ -2,7 +2,7 @@ Kernel driver i2c-ocores Supported adapters: * OpenCores.org I2C controller by Richard Herveille (see datasheet link) - Datasheet: http://www.opencores.org/projects.cgi/web/i2c/overview + https://opencores.org/project/i2c/overview Author: Peter Korsgaard diff --git a/Documentation/linux/Linux_sys_programming.pdf b/Documentation/linux/Linux_sys_programming.pdf new file mode 100644 index 00000000000000..b9bf4bd301a83e Binary files /dev/null and b/Documentation/linux/Linux_sys_programming.pdf differ diff --git a/Documentation/linux/linux_lernel_development_3rd.pdf b/Documentation/linux/linux_lernel_development_3rd.pdf new file mode 100644 index 00000000000000..9fbdfc4c649718 Binary files /dev/null and b/Documentation/linux/linux_lernel_development_3rd.pdf differ diff --git a/Documentation/networking/netdev-FAQ.txt b/Documentation/networking/netdev-FAQ.txt index 2a3278d5cf3537..fa951b820b25d9 100644 --- a/Documentation/networking/netdev-FAQ.txt +++ b/Documentation/networking/netdev-FAQ.txt @@ -179,6 +179,15 @@ A: No. See above answer. In short, if you think it really belongs in dash marker line as described in Documentation/process/submitting-patches.rst to temporarily embed that information into the patch that you send. +Q: Are all networking bug fixes backported to all stable releases? + +A: Due to capacity, Dave could only take care of the backports for the last + 2 stable releases. For earlier stable releases, each stable branch maintainer + is supposed to take care of them. If you find any patch is missing from an + earlier stable branch, please notify stable@vger.kernel.org with either a + commit ID or a formal patch backported, and CC Dave and other relevant + networking developers. + Q: Someone said that the comment style and coding convention is different for the networking content. Is this true? diff --git a/Documentation/networking/ppp_generic.txt b/Documentation/networking/ppp_generic.txt index 091d20273dcbbc..61daf4b3960091 100644 --- a/Documentation/networking/ppp_generic.txt +++ b/Documentation/networking/ppp_generic.txt @@ -300,12 +300,6 @@ unattached instance are: The ioctl calls available on an instance of /dev/ppp attached to a channel are: -* PPPIOCDETACH detaches the instance from the channel. This ioctl is - deprecated since the same effect can be achieved by closing the - instance. In order to prevent possible races this ioctl will fail - with an EINVAL error if more than one file descriptor refers to this - instance (i.e. as a result of dup(), dup2() or fork()). - * PPPIOCCONNECT connects this channel to a PPP interface. The argument should point to an int containing the interface unit number. It will return an EINVAL error if the channel is already diff --git a/Documentation/timers/ia-pc hpet_v1.pdf b/Documentation/timers/ia-pc hpet_v1.pdf new file mode 100644 index 00000000000000..f15d4a96206626 Binary files /dev/null and b/Documentation/timers/ia-pc hpet_v1.pdf differ diff --git a/Documentation/timers/intel-pit-8254.pdf b/Documentation/timers/intel-pit-8254.pdf new file mode 100644 index 00000000000000..78c6ed71589f76 Binary files /dev/null and b/Documentation/timers/intel-pit-8254.pdf differ diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspace-api/index.rst index 7b2eb1b7d4cab3..a3233da7fa88ed 100644 --- a/Documentation/userspace-api/index.rst +++ b/Documentation/userspace-api/index.rst @@ -19,6 +19,7 @@ place where this information is gathered. no_new_privs seccomp_filter unshare + spec_ctrl .. only:: subproject and html diff --git a/Documentation/userspace-api/spec_ctrl.rst b/Documentation/userspace-api/spec_ctrl.rst new file mode 100644 index 00000000000000..32f3d55c54b75e --- /dev/null +++ b/Documentation/userspace-api/spec_ctrl.rst @@ -0,0 +1,94 @@ +=================== +Speculation Control +=================== + +Quite some CPUs have speculation-related misfeatures which are in +fact vulnerabilities causing data leaks in various forms even across +privilege domains. + +The kernel provides mitigation for such vulnerabilities in various +forms. Some of these mitigations are compile-time configurable and some +can be supplied on the kernel command line. + +There is also a class of mitigations which are very expensive, but they can +be restricted to a certain set of processes or tasks in controlled +environments. The mechanism to control these mitigations is via +:manpage:`prctl(2)`. + +There are two prctl options which are related to this: + + * PR_GET_SPECULATION_CTRL + + * PR_SET_SPECULATION_CTRL + +PR_GET_SPECULATION_CTRL +----------------------- + +PR_GET_SPECULATION_CTRL returns the state of the speculation misfeature +which is selected with arg2 of prctl(2). The return value uses bits 0-3 with +the following meaning: + +==== ===================== =================================================== +Bit Define Description +==== ===================== =================================================== +0 PR_SPEC_PRCTL Mitigation can be controlled per task by + PR_SET_SPECULATION_CTRL. +1 PR_SPEC_ENABLE The speculation feature is enabled, mitigation is + disabled. +2 PR_SPEC_DISABLE The speculation feature is disabled, mitigation is + enabled. +3 PR_SPEC_FORCE_DISABLE Same as PR_SPEC_DISABLE, but cannot be undone. A + subsequent prctl(..., PR_SPEC_ENABLE) will fail. +==== ===================== =================================================== + +If all bits are 0 the CPU is not affected by the speculation misfeature. + +If PR_SPEC_PRCTL is set, then the per-task control of the mitigation is +available. If not set, prctl(PR_SET_SPECULATION_CTRL) for the speculation +misfeature will fail. + +PR_SET_SPECULATION_CTRL +----------------------- + +PR_SET_SPECULATION_CTRL allows to control the speculation misfeature, which +is selected by arg2 of :manpage:`prctl(2)` per task. arg3 is used to hand +in the control value, i.e. either PR_SPEC_ENABLE or PR_SPEC_DISABLE or +PR_SPEC_FORCE_DISABLE. + +Common error codes +------------------ +======= ================================================================= +Value Meaning +======= ================================================================= +EINVAL The prctl is not implemented by the architecture or unused + prctl(2) arguments are not 0. + +ENODEV arg2 is selecting a not supported speculation misfeature. +======= ================================================================= + +PR_SET_SPECULATION_CTRL error codes +----------------------------------- +======= ================================================================= +Value Meaning +======= ================================================================= +0 Success + +ERANGE arg3 is incorrect, i.e. it's neither PR_SPEC_ENABLE nor + PR_SPEC_DISABLE nor PR_SPEC_FORCE_DISABLE. + +ENXIO Control of the selected speculation misfeature is not possible. + See PR_GET_SPECULATION_CTRL. + +EPERM Speculation was disabled with PR_SPEC_FORCE_DISABLE and caller + tried to enable it again. +======= ================================================================= + +Speculation misfeature controls +------------------------------- +- PR_SPEC_STORE_BYPASS: Speculative Store Bypass + + Invocations: + * prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, 0, 0, 0); + * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_ENABLE, 0, 0); + * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_DISABLE, 0, 0); + * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_FORCE_DISABLE, 0, 0); diff --git a/Documentation/virtual/kvm/cpuid.txt b/Documentation/virtual/kvm/cpuid.txt index d4f33eb805dd22..ab022dcd091175 100644 --- a/Documentation/virtual/kvm/cpuid.txt +++ b/Documentation/virtual/kvm/cpuid.txt @@ -72,8 +72,8 @@ KVM_FEATURE_CLOCKSOURCE_STABLE_BIT || 24 || host will warn if no guest-side flag || value || meaning ================================================================================== -KVM_HINTS_DEDICATED || 0 || guest checks this feature bit to - || || determine if there is vCPU pinning - || || and there is no vCPU over-commitment, +KVM_HINTS_REALTIME || 0 || guest checks this feature bit to + || || determine that vCPUs are never + || || preempted for an unlimited time, || || allowing optimizations ---------------------------------------------------------------------------------- diff --git a/MAINTAINERS b/MAINTAINERS index df6e9bb2559afd..9c125f705f78f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -137,9 +137,9 @@ Maintainers List (try to look for most precise areas first) ----------------------------------- 3C59X NETWORK DRIVER -M: Steffen Klassert +M: Steffen Klassert L: netdev@vger.kernel.org -S: Maintained +S: Odd Fixes F: Documentation/networking/vortex.txt F: drivers/net/ethernet/3com/3c59x.c @@ -2332,7 +2332,7 @@ F: drivers/gpio/gpio-ath79.c F: Documentation/devicetree/bindings/gpio/gpio-ath79.txt ATHEROS ATH GENERIC UTILITIES -M: "Luis R. Rodriguez" +M: Kalle Valo L: linux-wireless@vger.kernel.org S: Supported F: drivers/net/wireless/ath/* @@ -2347,7 +2347,7 @@ S: Maintained F: drivers/net/wireless/ath/ath5k/ ATHEROS ATH6KL WIRELESS DRIVER -M: Kalle Valo +M: Kalle Valo L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/users/Drivers/ath6kl T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git @@ -3691,7 +3691,6 @@ F: drivers/cpufreq/arm_big_little_dt.c CPU POWER MONITORING SUBSYSTEM M: Thomas Renninger -M: Shuah Khan M: Shuah Khan L: linux-pm@vger.kernel.org S: Maintained @@ -4310,7 +4309,7 @@ F: Documentation/driver-api/dma-buf.rst T: git git://anongit.freedesktop.org/drm/drm-misc DMA GENERIC OFFLOAD ENGINE SUBSYSTEM -M: Vinod Koul +M: Vinod Koul L: dmaengine@vger.kernel.org Q: https://patchwork.kernel.org/project/linux-dmaengine/list/ S: Maintained @@ -5389,7 +5388,6 @@ S: Maintained F: drivers/iommu/exynos-iommu.c EZchip NPS platform support -M: Elad Kanfi M: Vineet Gupta S: Supported F: arch/arc/plat-eznps @@ -6505,9 +6503,15 @@ F: Documentation/networking/hinic.txt F: drivers/net/ethernet/huawei/hinic/ HUGETLB FILESYSTEM -M: Nadia Yvette Chambers +M: Mike Kravetz +L: linux-mm@kvack.org S: Maintained F: fs/hugetlbfs/ +F: mm/hugetlb.c +F: include/linux/hugetlb.h +F: Documentation/admin-guide/mm/hugetlbpage.rst +F: Documentation/vm/hugetlbfs_reserv.rst +F: Documentation/ABI/testing/sysfs-kernel-mm-hugepages HVA ST MEDIA DRIVER M: Jean-Christophe Trotin @@ -7696,10 +7700,10 @@ F: include/linux/sunrpc/ F: include/uapi/linux/sunrpc/ KERNEL SELFTEST FRAMEWORK -M: Shuah Khan M: Shuah Khan L: linux-kselftest@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git +Q: https://patchwork.kernel.org/project/linux-kselftest/list/ S: Maintained F: tools/testing/selftests/ F: Documentation/dev-tools/kselftest* @@ -9022,7 +9026,6 @@ Q: http://patchwork.ozlabs.org/project/netdev/list/ F: drivers/net/ethernet/mellanox/mlx5/core/en_* MELLANOX ETHERNET INNOVA DRIVER -M: Ilan Tayari R: Boris Pismenny L: netdev@vger.kernel.org S: Supported @@ -9032,7 +9035,6 @@ F: drivers/net/ethernet/mellanox/mlx5/core/fpga/* F: include/linux/mlx5/mlx5_ifc_fpga.h MELLANOX ETHERNET INNOVA IPSEC DRIVER -M: Ilan Tayari R: Boris Pismenny L: netdev@vger.kernel.org S: Supported @@ -9088,7 +9090,6 @@ F: include/uapi/rdma/mlx4-abi.h MELLANOX MLX5 core VPI driver M: Saeed Mahameed -M: Matan Barak M: Leon Romanovsky L: netdev@vger.kernel.org L: linux-rdma@vger.kernel.org @@ -9099,7 +9100,6 @@ F: drivers/net/ethernet/mellanox/mlx5/core/ F: include/linux/mlx5/ MELLANOX MLX5 IB driver -M: Matan Barak M: Leon Romanovsky L: linux-rdma@vger.kernel.org W: http://www.mellanox.com @@ -9833,7 +9833,6 @@ F: net/netfilter/xt_CONNSECMARK.c F: net/netfilter/xt_SECMARK.c NETWORKING [TLS] -M: Ilya Lesokhin M: Aviad Yehezkel M: Dave Watson L: netdev@vger.kernel.org @@ -9873,7 +9872,7 @@ F: include/linux/platform_data/nxp-nci.h F: Documentation/devicetree/bindings/net/nfc/ NFS, SUNRPC, AND LOCKD CLIENTS -M: Trond Myklebust +M: Trond Myklebust M: Anna Schumaker L: linux-nfs@vger.kernel.org W: http://client.linux-nfs.org @@ -11633,7 +11632,7 @@ S: Maintained F: drivers/media/tuners/qt1010* QUALCOMM ATHEROS ATH10K WIRELESS DRIVER -M: Kalle Valo +M: Kalle Valo L: ath10k@lists.infradead.org W: http://wireless.kernel.org/en/users/Drivers/ath10k T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git @@ -11684,7 +11683,7 @@ S: Maintained F: drivers/media/platform/qcom/venus/ QUALCOMM WCN36XX WIRELESS DRIVER -M: Eugene Krasnikov +M: Kalle Valo L: wcn36xx@lists.infradead.org W: http://wireless.kernel.org/en/users/Drivers/wcn36xx T: git git://github.com/KrasnikovEugene/wcn36xx.git @@ -12222,7 +12221,7 @@ F: Documentation/s390/vfio-ccw.txt F: include/uapi/linux/vfio_ccw.h S390 ZCRYPT DRIVER -M: Harald Freudenberger +M: Harald Freudenberger L: linux-s390@vger.kernel.org W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported @@ -13266,6 +13265,12 @@ M: Jan-Benedict Glaw S: Maintained F: arch/alpha/kernel/srm_env.c +ST STM32 I2C/SMBUS DRIVER +M: Pierre-Yves MORDRET +L: linux-i2c@vger.kernel.org +S: Maintained +F: drivers/i2c/busses/i2c-stm32* + STABLE BRANCH M: Greg Kroah-Hartman L: stable@vger.kernel.org @@ -14650,7 +14655,6 @@ F: drivers/usb/common/usb-otg-fsm.c USB OVER IP DRIVER M: Valentina Manea -M: Shuah Khan M: Shuah Khan L: linux-usb@vger.kernel.org S: Maintained @@ -15509,6 +15513,14 @@ L: linux-kernel@vger.kernel.org S: Supported F: drivers/char/xillybus/ +XLP9XX I2C DRIVER +M: George Cherian +M: Jan Glauber +L: linux-i2c@vger.kernel.org +W: http://www.cavium.com +S: Supported +F: drivers/i2c/busses/i2c-xlp9xx.c + XRA1403 GPIO EXPANDER M: Nandor Han M: Semi Malinen diff --git a/Makefile b/Makefile index d0d2652db174bf..31dc3a08295a76 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 4 PATCHLEVEL = 17 -SUBLEVEL = 0 -EXTRAVERSION = -rc4 +SUBLEVEL = 3 +EXTRAVERSION = NAME = Merciless Moray # *DOCUMENTATION* @@ -500,6 +500,9 @@ RETPOLINE_CFLAGS_CLANG := -mretpoline-external-thunk RETPOLINE_CFLAGS := $(call cc-option,$(RETPOLINE_CFLAGS_GCC),$(call cc-option,$(RETPOLINE_CFLAGS_CLANG))) export RETPOLINE_CFLAGS +KBUILD_CFLAGS += $(call cc-option,-fno-PIE) +KBUILD_AFLAGS += $(call cc-option,-fno-PIE) + # check for 'asm goto' ifeq ($(call shell-cached,$(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC) $(KBUILD_CFLAGS)), y) CC_HAVE_ASM_GOTO := 1 @@ -621,9 +624,9 @@ endif # $(dot-config) # Defaults to vmlinux, but the arch makefile usually adds further targets all: vmlinux -KBUILD_CFLAGS += $(call cc-option,-fno-PIE) -KBUILD_AFLAGS += $(call cc-option,-fno-PIE) -CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -fno-tree-loop-im $(call cc-disable-warning,maybe-uninitialized,) +CFLAGS_GCOV := -fprofile-arcs -ftest-coverage \ + $(call cc-option,-fno-tree-loop-im) \ + $(call cc-disable-warning,maybe-uninitialized,) export CFLAGS_GCOV CFLAGS_KCOV # The arch Makefile can set ARCH_{CPP,A,C}FLAGS to override the default diff --git a/arch/Kconfig b/arch/Kconfig index 8e0d665c8d53d3..75dd23acf133b0 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -464,6 +464,10 @@ config GCC_PLUGIN_LATENT_ENTROPY config GCC_PLUGIN_STRUCTLEAK bool "Force initialization of variables containing userspace addresses" depends on GCC_PLUGINS + # Currently STRUCTLEAK inserts initialization out of live scope of + # variables from KASAN point of view. This leads to KASAN false + # positive reports. Prohibit this combination for now. + depends on !KASAN_EXTRA help This plugin zero-initializes any structures containing a __user attribute. This can prevent some classes of information diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index b2022885ced8ab..f19dc31288c83e 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -211,6 +211,7 @@ config ALPHA_EIGER config ALPHA_JENSEN bool "Jensen" depends on BROKEN + select DMA_DIRECT_OPS help DEC PC 150 AXP (aka Jensen): This is a very old Digital system - one of the first-generation Alpha systems. A number of these systems diff --git a/arch/alpha/include/asm/dma-mapping.h b/arch/alpha/include/asm/dma-mapping.h index b78f61f20796b2..8beeafd4f68e45 100644 --- a/arch/alpha/include/asm/dma-mapping.h +++ b/arch/alpha/include/asm/dma-mapping.h @@ -2,11 +2,15 @@ #ifndef _ALPHA_DMA_MAPPING_H #define _ALPHA_DMA_MAPPING_H -extern const struct dma_map_ops *dma_ops; +extern const struct dma_map_ops alpha_pci_ops; static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) { - return dma_ops; +#ifdef CONFIG_ALPHA_JENSEN + return &dma_direct_ops; +#else + return &alpha_pci_ops; +#endif } #endif /* _ALPHA_DMA_MAPPING_H */ diff --git a/arch/alpha/kernel/io.c b/arch/alpha/kernel/io.c index 3e3d49c254c52e..c025a3e5e3578b 100644 --- a/arch/alpha/kernel/io.c +++ b/arch/alpha/kernel/io.c @@ -37,20 +37,20 @@ unsigned int ioread32(void __iomem *addr) void iowrite8(u8 b, void __iomem *addr) { - IO_CONCAT(__IO_PREFIX,iowrite8)(b, addr); mb(); + IO_CONCAT(__IO_PREFIX,iowrite8)(b, addr); } void iowrite16(u16 b, void __iomem *addr) { - IO_CONCAT(__IO_PREFIX,iowrite16)(b, addr); mb(); + IO_CONCAT(__IO_PREFIX,iowrite16)(b, addr); } void iowrite32(u32 b, void __iomem *addr) { - IO_CONCAT(__IO_PREFIX,iowrite32)(b, addr); mb(); + IO_CONCAT(__IO_PREFIX,iowrite32)(b, addr); } EXPORT_SYMBOL(ioread8); @@ -176,26 +176,26 @@ u64 readq(const volatile void __iomem *addr) void writeb(u8 b, volatile void __iomem *addr) { - __raw_writeb(b, addr); mb(); + __raw_writeb(b, addr); } void writew(u16 b, volatile void __iomem *addr) { - __raw_writew(b, addr); mb(); + __raw_writew(b, addr); } void writel(u32 b, volatile void __iomem *addr) { - __raw_writel(b, addr); mb(); + __raw_writel(b, addr); } void writeq(u64 b, volatile void __iomem *addr) { - __raw_writeq(b, addr); mb(); + __raw_writeq(b, addr); } EXPORT_SYMBOL(readb); diff --git a/arch/alpha/kernel/pci-noop.c b/arch/alpha/kernel/pci-noop.c index b6ebb65127a80e..c7c5879869d350 100644 --- a/arch/alpha/kernel/pci-noop.c +++ b/arch/alpha/kernel/pci-noop.c @@ -102,36 +102,3 @@ SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn, else return -ENODEV; } - -static void *alpha_noop_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp, - unsigned long attrs) -{ - void *ret; - - if (!dev || *dev->dma_mask >= 0xffffffffUL) - gfp &= ~GFP_DMA; - ret = (void *)__get_free_pages(gfp, get_order(size)); - if (ret) { - memset(ret, 0, size); - *dma_handle = virt_to_phys(ret); - } - return ret; -} - -static int alpha_noop_supported(struct device *dev, u64 mask) -{ - return mask < 0x00ffffffUL ? 0 : 1; -} - -const struct dma_map_ops alpha_noop_ops = { - .alloc = alpha_noop_alloc_coherent, - .free = dma_noop_free_coherent, - .map_page = dma_noop_map_page, - .map_sg = dma_noop_map_sg, - .mapping_error = dma_noop_mapping_error, - .dma_supported = alpha_noop_supported, -}; - -const struct dma_map_ops *dma_ops = &alpha_noop_ops; -EXPORT_SYMBOL(dma_ops); diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c index 83b34b9188ea19..6923b0d9c1e195 100644 --- a/arch/alpha/kernel/pci_iommu.c +++ b/arch/alpha/kernel/pci_iommu.c @@ -950,6 +950,4 @@ const struct dma_map_ops alpha_pci_ops = { .mapping_error = alpha_pci_mapping_error, .dma_supported = alpha_pci_supported, }; - -const struct dma_map_ops *dma_ops = &alpha_pci_ops; -EXPORT_SYMBOL(dma_ops); +EXPORT_SYMBOL(alpha_pci_ops); diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 45a6b9b7af2a56..6a4e7341ecd332 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -117,11 +117,9 @@ ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) asflags-y := -DZIMAGE # Supply kernel BSS size to the decompressor via a linker symbol. -KBSS_SZ = $(shell $(CROSS_COMPILE)nm $(obj)/../../../../vmlinux | \ - perl -e 'while (<>) { \ - $$bss_start=hex($$1) if /^([[:xdigit:]]+) B __bss_start$$/; \ - $$bss_end=hex($$1) if /^([[:xdigit:]]+) B __bss_stop$$/; \ - }; printf "%d\n", $$bss_end - $$bss_start;') +KBSS_SZ = $(shell echo $$(($$($(CROSS_COMPILE)nm $(obj)/../../../../vmlinux | \ + sed -n -e 's/^\([^ ]*\) [AB] __bss_start$$/-0x\1/p' \ + -e 's/^\([^ ]*\) [AB] __bss_stop$$/+0x\1/p') )) ) LDFLAGS_vmlinux = --defsym _kernel_bss_size=$(KBSS_SZ) # Supply ZRELADDR to the decompressor via a linker symbol. ifneq ($(CONFIG_AUTO_ZRELADDR),y) diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 45c8823c37503d..517e0e18f0b830 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -29,19 +29,19 @@ #if defined(CONFIG_DEBUG_ICEDCC) #if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V6K) || defined(CONFIG_CPU_V7) - .macro loadsp, rb, tmp + .macro loadsp, rb, tmp1, tmp2 .endm .macro writeb, ch, rb mcr p14, 0, \ch, c0, c5, 0 .endm #elif defined(CONFIG_CPU_XSCALE) - .macro loadsp, rb, tmp + .macro loadsp, rb, tmp1, tmp2 .endm .macro writeb, ch, rb mcr p14, 0, \ch, c8, c0, 0 .endm #else - .macro loadsp, rb, tmp + .macro loadsp, rb, tmp1, tmp2 .endm .macro writeb, ch, rb mcr p14, 0, \ch, c1, c0, 0 @@ -57,7 +57,7 @@ .endm #if defined(CONFIG_ARCH_SA1100) - .macro loadsp, rb, tmp + .macro loadsp, rb, tmp1, tmp2 mov \rb, #0x80000000 @ physical base address #ifdef CONFIG_DEBUG_LL_SER3 add \rb, \rb, #0x00050000 @ Ser3 @@ -66,8 +66,8 @@ #endif .endm #else - .macro loadsp, rb, tmp - addruart \rb, \tmp + .macro loadsp, rb, tmp1, tmp2 + addruart \rb, \tmp1, \tmp2 .endm #endif #endif @@ -561,8 +561,6 @@ not_relocated: mov r0, #0 bl decompress_kernel bl cache_clean_flush bl cache_off - mov r1, r7 @ restore architecture number - mov r2, r8 @ restore atags pointer #ifdef CONFIG_ARM_VIRT_EXT mrs r0, spsr @ Get saved CPU boot mode @@ -1297,7 +1295,7 @@ phex: adr r3, phexbuf b 1b @ puts corrupts {r0, r1, r2, r3} -puts: loadsp r3, r1 +puts: loadsp r3, r2, r1 1: ldrb r2, [r0], #1 teq r2, #0 moveq pc, lr @@ -1314,8 +1312,8 @@ puts: loadsp r3, r1 @ putc corrupts {r0, r1, r2, r3} putc: mov r2, r0 + loadsp r3, r1, r0 mov r0, #0 - loadsp r3, r1 b 2b @ memdump corrupts {r0, r1, r2, r3, r10, r11, r12, lr} @@ -1365,6 +1363,8 @@ __hyp_reentry_vectors: __enter_kernel: mov r0, #0 @ must be 0 + mov r1, r7 @ restore architecture number + mov r2, r8 @ restore atags pointer ARM( mov pc, r4 ) @ call kernel M_CLASS( add r4, r4, #1 ) @ enter in Thumb mode for M class THUMB( bx r4 ) @ entry point is always ARM for A/R classes diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 699fdf94d139bd..9fe4f5a6379e3b 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -69,7 +69,7 @@ timer@20200 { compatible = "arm,cortex-a9-global-timer"; reg = <0x20200 0x100>; - interrupts = ; + interrupts = ; clocks = <&periph_clk>; }; diff --git a/arch/arm/boot/dts/da850-lcdk.dts b/arch/arm/boot/dts/da850-lcdk.dts index a1f4d6d5a569bb..0edf769ea95c93 100644 --- a/arch/arm/boot/dts/da850-lcdk.dts +++ b/arch/arm/boot/dts/da850-lcdk.dts @@ -21,8 +21,8 @@ stdout-path = "serial2:115200n8"; }; - memory { - device_type = "memory"; + memory@c0000000 { + /* 128 MB DDR2 SDRAM @ 0xc0000000 */ reg = <0xc0000000 0x08000000>; }; diff --git a/arch/arm/boot/dts/da850.dtsi b/arch/arm/boot/dts/da850.dtsi index c66cf78953639d..12010002dbdb65 100644 --- a/arch/arm/boot/dts/da850.dtsi +++ b/arch/arm/boot/dts/da850.dtsi @@ -7,10 +7,19 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ -#include "skeleton.dtsi" #include / { + #address-cells = <1>; + #size-cells = <1>; + chosen { }; + aliases { }; + + memory@c0000000 { + device_type = "memory"; + reg = <0xc0000000 0x0>; + }; + arm { #address-cells = <1>; #size-cells = <1>; @@ -46,8 +55,6 @@ pmx_core: pinmux@14120 { compatible = "pinctrl-single"; reg = <0x14120 0x50>; - #address-cells = <1>; - #size-cells = <0>; #pinctrl-cells = <2>; pinctrl-single,bit-per-mux; pinctrl-single,register-width = <32>; diff --git a/arch/arm/boot/dts/dm8148-evm.dts b/arch/arm/boot/dts/dm8148-evm.dts index d6657b3bae84b1..85d7b5148b0ac6 100644 --- a/arch/arm/boot/dts/dm8148-evm.dts +++ b/arch/arm/boot/dts/dm8148-evm.dts @@ -10,7 +10,7 @@ / { model = "DM8148 EVM"; - compatible = "ti,dm8148-evm", "ti,dm8148"; + compatible = "ti,dm8148-evm", "ti,dm8148", "ti,dm814"; memory@80000000 { device_type = "memory"; diff --git a/arch/arm/boot/dts/dm8148-t410.dts b/arch/arm/boot/dts/dm8148-t410.dts index 63883b3479f95f..6418f9cdbe83fa 100644 --- a/arch/arm/boot/dts/dm8148-t410.dts +++ b/arch/arm/boot/dts/dm8148-t410.dts @@ -9,7 +9,7 @@ / { model = "HP t410 Smart Zero Client"; - compatible = "hp,t410", "ti,dm8148"; + compatible = "hp,t410", "ti,dm8148", "ti,dm814"; memory@80000000 { device_type = "memory"; diff --git a/arch/arm/boot/dts/dm8168-evm.dts b/arch/arm/boot/dts/dm8168-evm.dts index c72a2132aa823b..1d030d56730717 100644 --- a/arch/arm/boot/dts/dm8168-evm.dts +++ b/arch/arm/boot/dts/dm8168-evm.dts @@ -10,7 +10,7 @@ / { model = "DM8168 EVM"; - compatible = "ti,dm8168-evm", "ti,dm8168"; + compatible = "ti,dm8168-evm", "ti,dm8168", "ti,dm816"; memory@80000000 { device_type = "memory"; diff --git a/arch/arm/boot/dts/dra62x-j5eco-evm.dts b/arch/arm/boot/dts/dra62x-j5eco-evm.dts index fee0547f7302ec..31b824ad5d29fa 100644 --- a/arch/arm/boot/dts/dra62x-j5eco-evm.dts +++ b/arch/arm/boot/dts/dra62x-j5eco-evm.dts @@ -10,7 +10,7 @@ / { model = "DRA62x J5 Eco EVM"; - compatible = "ti,dra62x-j5eco-evm", "ti,dra62x", "ti,dm8148"; + compatible = "ti,dra62x-j5eco-evm", "ti,dra62x", "ti,dm8148", "ti,dm814"; memory@80000000 { device_type = "memory"; diff --git a/arch/arm/boot/dts/imx35.dtsi b/arch/arm/boot/dts/imx35.dtsi index bf343195697e8c..54111ed218b10f 100644 --- a/arch/arm/boot/dts/imx35.dtsi +++ b/arch/arm/boot/dts/imx35.dtsi @@ -303,7 +303,7 @@ }; can1: can@53fe4000 { - compatible = "fsl,imx35-flexcan"; + compatible = "fsl,imx35-flexcan", "fsl,imx25-flexcan"; reg = <0x53fe4000 0x1000>; clocks = <&clks 33>, <&clks 33>; clock-names = "ipg", "per"; @@ -312,7 +312,7 @@ }; can2: can@53fe8000 { - compatible = "fsl,imx35-flexcan"; + compatible = "fsl,imx35-flexcan", "fsl,imx25-flexcan"; reg = <0x53fe8000 0x1000>; clocks = <&clks 34>, <&clks 34>; clock-names = "ipg", "per"; diff --git a/arch/arm/boot/dts/imx51-zii-rdu1.dts b/arch/arm/boot/dts/imx51-zii-rdu1.dts index 0c99ac04ad08b2..6464f2560e066b 100644 --- a/arch/arm/boot/dts/imx51-zii-rdu1.dts +++ b/arch/arm/boot/dts/imx51-zii-rdu1.dts @@ -523,7 +523,7 @@ }; touchscreen@20 { - compatible = "syna,rmi4_i2c"; + compatible = "syna,rmi4-i2c"; reg = <0x20>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ts>; @@ -541,8 +541,8 @@ rmi4-f11@11 { reg = <0x11>; - touch-inverted-y; - touch-swapped-x-y; + touchscreen-inverted-y; + touchscreen-swapped-x-y; syna,sensor-type = <1>; }; }; diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi index 7d647d043f528a..3d65c0192f6931 100644 --- a/arch/arm/boot/dts/imx53.dtsi +++ b/arch/arm/boot/dts/imx53.dtsi @@ -551,7 +551,7 @@ }; can1: can@53fc8000 { - compatible = "fsl,imx53-flexcan"; + compatible = "fsl,imx53-flexcan", "fsl,imx25-flexcan"; reg = <0x53fc8000 0x4000>; interrupts = <82>; clocks = <&clks IMX5_CLK_CAN1_IPG_GATE>, @@ -561,7 +561,7 @@ }; can2: can@53fcc000 { - compatible = "fsl,imx53-flexcan"; + compatible = "fsl,imx53-flexcan", "fsl,imx25-flexcan"; reg = <0x53fcc000 0x4000>; interrupts = <83>; clocks = <&clks IMX5_CLK_CAN2_IPG_GATE>, diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi index 4d42335c0dee99..ce85b3ca1a55fc 100644 --- a/arch/arm/boot/dts/imx7s.dtsi +++ b/arch/arm/boot/dts/imx7s.dtsi @@ -868,6 +868,7 @@ crypto: caam@30900000 { compatible = "fsl,sec-v4.0"; + fsl,sec-era = <8>; #address-cells = <1>; #size-cells = <1>; reg = <0x30900000 0x40000>; diff --git a/arch/arm/boot/dts/logicpd-som-lv.dtsi b/arch/arm/boot/dts/logicpd-som-lv.dtsi index b47cac23a04be5..6fa7bba3e80150 100644 --- a/arch/arm/boot/dts/logicpd-som-lv.dtsi +++ b/arch/arm/boot/dts/logicpd-som-lv.dtsi @@ -26,7 +26,7 @@ gpio = <&gpio1 3 0>; /* gpio_3 */ startup-delay-us = <70000>; enable-active-high; - vin-supply = <&vmmc2>; + vin-supply = <&vaux3>; }; /* HS USB Host PHY on PORT 1 */ @@ -82,6 +82,7 @@ twl_audio: audio { compatible = "ti,twl4030-audio"; codec { + ti,hs_extmute_gpio = <&gpio2 25 GPIO_ACTIVE_HIGH>; }; }; }; @@ -199,6 +200,7 @@ pinctrl-single,pins = < OMAP3_CORE1_IOPAD(0x21ba, PIN_INPUT | MUX_MODE0) /* i2c1_scl.i2c1_scl */ OMAP3_CORE1_IOPAD(0x21bc, PIN_INPUT | MUX_MODE0) /* i2c1_sda.i2c1_sda */ + OMAP3_CORE1_IOPAD(0x20ba, PIN_OUTPUT | MUX_MODE4) /* gpmc_ncs6.gpio_57 */ >; }; }; @@ -213,7 +215,7 @@ }; wl127x_gpio: pinmux_wl127x_gpio_pin { pinctrl-single,pins = < - OMAP3_WKUP_IOPAD(0x2a0c, PIN_INPUT | MUX_MODE4) /* sys_boot0.gpio_2 */ + OMAP3_WKUP_IOPAD(0x2a0a, PIN_INPUT | MUX_MODE4) /* sys_boot0.gpio_2 */ OMAP3_WKUP_IOPAD(0x2a0c, PIN_OUTPUT | MUX_MODE4) /* sys_boot1.gpio_3 */ >; }; @@ -260,6 +262,11 @@ #include "twl4030.dtsi" #include "twl4030_omap3.dtsi" +&vaux3 { + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; +}; + &twl { twl_power: power { compatible = "ti,twl4030-power-idle-osc-off", "ti,twl4030-power-idle"; diff --git a/arch/arm/boot/dts/r8a7790-lager.dts b/arch/arm/boot/dts/r8a7790-lager.dts index 063fdb65dc60df..f07f9018c3e72e 100644 --- a/arch/arm/boot/dts/r8a7790-lager.dts +++ b/arch/arm/boot/dts/r8a7790-lager.dts @@ -379,7 +379,7 @@ port@0 { reg = <0>; adv7511_in: endpoint { - remote-endpoint = <&du_out_lvds0>; + remote-endpoint = <&lvds0_out>; }; }; @@ -467,10 +467,8 @@ status = "okay"; clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&cpg CPG_MOD 722>, - <&cpg CPG_MOD 726>, <&cpg CPG_MOD 725>, <&x13_clk>, <&x2_clk>; - clock-names = "du.0", "du.1", "du.2", "lvds.0", "lvds.1", - "dclkin.0", "dclkin.1"; + clock-names = "du.0", "du.1", "du.2", "dclkin.0", "dclkin.1"; ports { port@0 { @@ -478,12 +476,26 @@ remote-endpoint = <&adv7123_in>; }; }; + }; +}; + +&lvds0 { + status = "okay"; + + ports { port@1 { endpoint { remote-endpoint = <&adv7511_in>; }; }; - port@2 { + }; +}; + +&lvds1 { + status = "okay"; + + ports { + port@1 { lvds_connector: endpoint { }; }; diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index e4367cecad18a1..05a0fc23ac88f8 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -1627,18 +1627,13 @@ du: display@feb00000 { compatible = "renesas,du-r8a7790"; - reg = <0 0xfeb00000 0 0x70000>, - <0 0xfeb90000 0 0x1c>, - <0 0xfeb94000 0 0x1c>; - reg-names = "du", "lvds.0", "lvds.1"; + reg = <0 0xfeb00000 0 0x70000>; interrupts = , , ; clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, - <&cpg CPG_MOD 722>, <&cpg CPG_MOD 726>, - <&cpg CPG_MOD 725>; - clock-names = "du.0", "du.1", "du.2", "lvds.0", - "lvds.1"; + <&cpg CPG_MOD 722>; + clock-names = "du.0", "du.1", "du.2"; status = "disabled"; ports { @@ -1653,11 +1648,65 @@ port@1 { reg = <1>; du_out_lvds0: endpoint { + remote-endpoint = <&lvds0_in>; }; }; port@2 { reg = <2>; du_out_lvds1: endpoint { + remote-endpoint = <&lvds1_in>; + }; + }; + }; + }; + + lvds0: lvds@feb90000 { + compatible = "renesas,r8a7790-lvds"; + reg = <0 0xfeb90000 0 0x1c>; + clocks = <&cpg CPG_MOD 726>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 726>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds0_in: endpoint { + remote-endpoint = <&du_out_lvds0>; + }; + }; + port@1 { + reg = <1>; + lvds0_out: endpoint { + }; + }; + }; + }; + + lvds1: lvds@feb94000 { + compatible = "renesas,r8a7790-lvds"; + reg = <0 0xfeb94000 0 0x1c>; + clocks = <&cpg CPG_MOD 725>; + power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; + resets = <&cpg 725>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds1_in: endpoint { + remote-endpoint = <&du_out_lvds1>; + }; + }; + port@1 { + reg = <1>; + lvds1_out: endpoint { }; }; }; diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts index f40321a1c917e7..9d7213a0b8b826 100644 --- a/arch/arm/boot/dts/r8a7791-koelsch.dts +++ b/arch/arm/boot/dts/r8a7791-koelsch.dts @@ -468,10 +468,9 @@ pinctrl-names = "default"; status = "okay"; - clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&cpg CPG_MOD 726>, + clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&x13_clk>, <&x2_clk>; - clock-names = "du.0", "du.1", "lvds.0", - "dclkin.0", "dclkin.1"; + clock-names = "du.0", "du.1", "dclkin.0", "dclkin.1"; ports { port@0 { @@ -479,6 +478,13 @@ remote-endpoint = <&adv7511_in>; }; }; + }; +}; + +&lvds0 { + status = "okay"; + + ports { port@1 { lvds_connector: endpoint { }; diff --git a/arch/arm/boot/dts/r8a7791-porter.dts b/arch/arm/boot/dts/r8a7791-porter.dts index c14e6fe9e4f69b..ae9ed9ff53efde 100644 --- a/arch/arm/boot/dts/r8a7791-porter.dts +++ b/arch/arm/boot/dts/r8a7791-porter.dts @@ -441,10 +441,9 @@ pinctrl-names = "default"; status = "okay"; - clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&cpg CPG_MOD 726>, + clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&x3_clk>, <&x16_clk>; - clock-names = "du.0", "du.1", "lvds.0", - "dclkin.0", "dclkin.1"; + clock-names = "du.0", "du.1", "dclkin.0", "dclkin.1"; ports { port@0 { @@ -455,6 +454,17 @@ }; }; +&lvds0 { + status = "okay"; + + ports { + port@1 { + lvds_connector: endpoint { + }; + }; + }; +}; + &rcar_sound { pinctrl-0 = <&ssi_pins &audio_clk_pins>; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index f11dab71b03a9f..506b2088541339 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -1633,15 +1633,12 @@ du: display@feb00000 { compatible = "renesas,du-r8a7791"; - reg = <0 0xfeb00000 0 0x40000>, - <0 0xfeb90000 0 0x1c>; - reg-names = "du", "lvds.0"; + reg = <0 0xfeb00000 0 0x40000>; interrupts = , ; clocks = <&cpg CPG_MOD 724>, - <&cpg CPG_MOD 723>, - <&cpg CPG_MOD 726>; - clock-names = "du.0", "du.1", "lvds.0"; + <&cpg CPG_MOD 723>; + clock-names = "du.0", "du.1"; status = "disabled"; ports { @@ -1656,6 +1653,33 @@ port@1 { reg = <1>; du_out_lvds0: endpoint { + remote-endpoint = <&lvds0_in>; + }; + }; + }; + }; + + lvds0: lvds@feb90000 { + compatible = "renesas,r8a7791-lvds"; + reg = <0 0xfeb90000 0 0x1c>; + clocks = <&cpg CPG_MOD 726>; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 726>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds0_in: endpoint { + remote-endpoint = <&du_out_lvds0>; + }; + }; + port@1 { + reg = <1>; + lvds0_out: endpoint { }; }; }; diff --git a/arch/arm/boot/dts/r8a7793-gose.dts b/arch/arm/boot/dts/r8a7793-gose.dts index 9ed6961f2d9a2c..96e117d8b2cce0 100644 --- a/arch/arm/boot/dts/r8a7793-gose.dts +++ b/arch/arm/boot/dts/r8a7793-gose.dts @@ -447,10 +447,9 @@ pinctrl-names = "default"; status = "okay"; - clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&cpg CPG_MOD 726>, + clocks = <&cpg CPG_MOD 724>, <&cpg CPG_MOD 723>, <&x13_clk>, <&x2_clk>; - clock-names = "du.0", "du.1", "lvds.0", - "dclkin.0", "dclkin.1"; + clock-names = "du.0", "du.1", "dclkin.0", "dclkin.1"; ports { port@0 { @@ -458,6 +457,11 @@ remote-endpoint = <&adv7511_in>; }; }; + }; +}; + +&lvds0 { + ports { port@1 { lvds_connector: endpoint { }; diff --git a/arch/arm/boot/dts/r8a7793.dtsi b/arch/arm/boot/dts/r8a7793.dtsi index f9c5a557107d93..4f526030dc7cb7 100644 --- a/arch/arm/boot/dts/r8a7793.dtsi +++ b/arch/arm/boot/dts/r8a7793.dtsi @@ -1292,15 +1292,12 @@ du: display@feb00000 { compatible = "renesas,du-r8a7793"; - reg = <0 0xfeb00000 0 0x40000>, - <0 0xfeb90000 0 0x1c>; - reg-names = "du", "lvds.0"; + reg = <0 0xfeb00000 0 0x40000>; interrupts = , ; clocks = <&cpg CPG_MOD 724>, - <&cpg CPG_MOD 723>, - <&cpg CPG_MOD 726>; - clock-names = "du.0", "du.1", "lvds.0"; + <&cpg CPG_MOD 723>; + clock-names = "du.0", "du.1"; status = "disabled"; ports { @@ -1315,6 +1312,34 @@ port@1 { reg = <1>; du_out_lvds0: endpoint { + remote-endpoint = <&lvds0_in>; + }; + }; + }; + }; + + lvds0: lvds@feb90000 { + compatible = "renesas,r8a7793-lvds"; + reg = <0 0xfeb90000 0 0x1c>; + clocks = <&cpg CPG_MOD 726>; + power-domains = <&sysc R8A7793_PD_ALWAYS_ON>; + resets = <&cpg 726>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds0_in: endpoint { + remote-endpoint = <&du_out_lvds0>; + }; + }; + port@1 { + reg = <1>; + lvds0_out: endpoint { }; }; }; diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi index 77e8436beed46c..3a1c6b45c9a1cd 100644 --- a/arch/arm/boot/dts/sun4i-a10.dtsi +++ b/arch/arm/boot/dts/sun4i-a10.dtsi @@ -76,7 +76,7 @@ allwinner,pipeline = "de_fe0-de_be0-lcd0-hdmi"; clocks = <&ccu CLK_AHB_LCD0>, <&ccu CLK_AHB_HDMI0>, <&ccu CLK_AHB_DE_BE0>, <&ccu CLK_AHB_DE_FE0>, - <&ccu CLK_DE_BE0>, <&ccu CLK_AHB_DE_FE0>, + <&ccu CLK_DE_BE0>, <&ccu CLK_DE_FE0>, <&ccu CLK_TCON0_CH1>, <&ccu CLK_HDMI>, <&ccu CLK_DRAM_DE_FE0>, <&ccu CLK_DRAM_DE_BE0>; status = "disabled"; @@ -88,7 +88,7 @@ allwinner,pipeline = "de_fe0-de_be0-lcd0"; clocks = <&ccu CLK_AHB_LCD0>, <&ccu CLK_AHB_DE_BE0>, <&ccu CLK_AHB_DE_FE0>, <&ccu CLK_DE_BE0>, - <&ccu CLK_AHB_DE_FE0>, <&ccu CLK_TCON0_CH0>, + <&ccu CLK_DE_FE0>, <&ccu CLK_TCON0_CH0>, <&ccu CLK_DRAM_DE_FE0>, <&ccu CLK_DRAM_DE_BE0>; status = "disabled"; }; @@ -99,7 +99,7 @@ allwinner,pipeline = "de_fe0-de_be0-lcd0-tve0"; clocks = <&ccu CLK_AHB_TVE0>, <&ccu CLK_AHB_LCD0>, <&ccu CLK_AHB_DE_BE0>, <&ccu CLK_AHB_DE_FE0>, - <&ccu CLK_DE_BE0>, <&ccu CLK_AHB_DE_FE0>, + <&ccu CLK_DE_BE0>, <&ccu CLK_DE_FE0>, <&ccu CLK_TCON0_CH1>, <&ccu CLK_DRAM_TVE0>, <&ccu CLK_DRAM_DE_FE0>, <&ccu CLK_DRAM_DE_BE0>; status = "disabled"; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts index 3328fe583c9ba3..232f124ce62c0d 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts @@ -117,6 +117,7 @@ phy-handle = <&int_mii_phy>; phy-mode = "mii"; allwinner,leds-active-low; + status = "okay"; }; &hdmi { diff --git a/arch/arm/boot/dts/sun8i-v3s-licheepi-zero-dock.dts b/arch/arm/boot/dts/sun8i-v3s-licheepi-zero-dock.dts index d1311098ea459b..ad173605b1b890 100644 --- a/arch/arm/boot/dts/sun8i-v3s-licheepi-zero-dock.dts +++ b/arch/arm/boot/dts/sun8i-v3s-licheepi-zero-dock.dts @@ -51,7 +51,7 @@ leds { /* The LEDs use PG0~2 pins, which conflict with MMC1 */ - status = "disbaled"; + status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 0a7136462a1a6d..983dd5c1479459 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -741,7 +741,7 @@ phy_type = "ulpi"; clocks = <&tegra_car TEGRA20_CLK_USB2>, <&tegra_car TEGRA20_CLK_PLL_U>, - <&tegra_car TEGRA20_CLK_PLL_P_OUT4>; + <&tegra_car TEGRA20_CLK_CDEV2>; clock-names = "reg", "pll_u", "ulpi-link"; resets = <&tegra_car 58>, <&tegra_car 22>; reset-names = "usb", "utmi-pads"; diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index bc8d4bbd82e277..9342904cccca67 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -536,4 +536,14 @@ THUMB( orr \reg , \reg , #PSR_T_BIT ) #endif .endm +#ifdef CONFIG_KPROBES +#define _ASM_NOKPROBE(entry) \ + .pushsection "_kprobe_blacklist", "aw" ; \ + .balign 4 ; \ + .long entry; \ + .popsection +#else +#define _ASM_NOKPROBE(entry) +#endif + #endif /* __ASM_ASSEMBLER_H__ */ diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h index 707a1f06dc5d5e..f675162663f09a 100644 --- a/arch/arm/include/asm/kvm_mmu.h +++ b/arch/arm/include/asm/kvm_mmu.h @@ -309,6 +309,22 @@ static inline unsigned int kvm_get_vmid_bits(void) return 8; } +/* + * We are not in the kvm->srcu critical section most of the time, so we take + * the SRCU read lock here. Since we copy the data from the user page, we + * can immediately drop the lock again. + */ +static inline int kvm_read_guest_lock(struct kvm *kvm, + gpa_t gpa, void *data, unsigned long len) +{ + int srcu_idx = srcu_read_lock(&kvm->srcu); + int ret = kvm_read_guest(kvm, gpa, data, len); + + srcu_read_unlock(&kvm->srcu, srcu_idx); + + return ret; +} + static inline void *kvm_get_hyp_vector(void) { return kvm_ksym_ref(__kvm_hyp_vector); diff --git a/arch/arm/include/uapi/asm/siginfo.h b/arch/arm/include/uapi/asm/siginfo.h deleted file mode 100644 index d0513880be210c..00000000000000 --- a/arch/arm/include/uapi/asm/siginfo.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __ASM_SIGINFO_H -#define __ASM_SIGINFO_H - -#include - -/* - * SIGFPE si_codes - */ -#ifdef __KERNEL__ -#define FPE_FIXME 0 /* Broken dup of SI_USER */ -#endif /* __KERNEL__ */ - -#endif diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index 6b38d7a634c19f..dd2eb5f76b9f0a 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -83,7 +83,7 @@ void machine_crash_nonpanic_core(void *unused) { struct pt_regs regs; - crash_setup_regs(®s, NULL); + crash_setup_regs(®s, get_irq_regs()); printk(KERN_DEBUG "CPU %u will stop doing anything useful since another CPU has crashed\n", smp_processor_id()); crash_save_cpu(®s, smp_processor_id()); @@ -95,6 +95,27 @@ void machine_crash_nonpanic_core(void *unused) cpu_relax(); } +void crash_smp_send_stop(void) +{ + static int cpus_stopped; + unsigned long msecs; + + if (cpus_stopped) + return; + + atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); + smp_call_function(machine_crash_nonpanic_core, NULL, false); + msecs = 1000; /* Wait at most a second for the other cpus to stop */ + while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) { + mdelay(1); + msecs--; + } + if (atomic_read(&waiting_for_crash_ipi) > 0) + pr_warn("Non-crashing CPUs did not react to IPI\n"); + + cpus_stopped = 1; +} + static void machine_kexec_mask_interrupts(void) { unsigned int i; @@ -120,19 +141,8 @@ static void machine_kexec_mask_interrupts(void) void machine_crash_shutdown(struct pt_regs *regs) { - unsigned long msecs; - local_irq_disable(); - - atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); - smp_call_function(machine_crash_nonpanic_core, NULL, false); - msecs = 1000; /* Wait at most a second for the other cpus to stop */ - while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) { - mdelay(1); - msecs--; - } - if (atomic_read(&waiting_for_crash_ipi) > 0) - pr_warn("Non-crashing CPUs did not react to IPI\n"); + crash_smp_send_stop(); crash_save_cpu(regs, smp_processor_id()); machine_kexec_mask_interrupts(); diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 5e3633c24e6365..2fe87109ae468b 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -417,7 +418,8 @@ void unregister_undef_hook(struct undef_hook *hook) raw_spin_unlock_irqrestore(&undef_lock, flags); } -static int call_undef_hook(struct pt_regs *regs, unsigned int instr) +static nokprobe_inline +int call_undef_hook(struct pt_regs *regs, unsigned int instr) { struct undef_hook *hook; unsigned long flags; @@ -490,6 +492,7 @@ asmlinkage void do_undefinstr(struct pt_regs *regs) arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6); } +NOKPROBE_SYMBOL(do_undefinstr) /* * Handle FIQ similarly to NMI on x86 systems. diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S index df73914e81c834..746e7801dcdf70 100644 --- a/arch/arm/lib/getuser.S +++ b/arch/arm/lib/getuser.S @@ -38,6 +38,7 @@ ENTRY(__get_user_1) mov r0, #0 ret lr ENDPROC(__get_user_1) +_ASM_NOKPROBE(__get_user_1) ENTRY(__get_user_2) check_uaccess r0, 2, r1, r2, __get_user_bad @@ -58,6 +59,7 @@ rb .req r0 mov r0, #0 ret lr ENDPROC(__get_user_2) +_ASM_NOKPROBE(__get_user_2) ENTRY(__get_user_4) check_uaccess r0, 4, r1, r2, __get_user_bad @@ -65,6 +67,7 @@ ENTRY(__get_user_4) mov r0, #0 ret lr ENDPROC(__get_user_4) +_ASM_NOKPROBE(__get_user_4) ENTRY(__get_user_8) check_uaccess r0, 8, r1, r2, __get_user_bad8 @@ -78,6 +81,7 @@ ENTRY(__get_user_8) mov r0, #0 ret lr ENDPROC(__get_user_8) +_ASM_NOKPROBE(__get_user_8) #ifdef __ARMEB__ ENTRY(__get_user_32t_8) @@ -91,6 +95,7 @@ ENTRY(__get_user_32t_8) mov r0, #0 ret lr ENDPROC(__get_user_32t_8) +_ASM_NOKPROBE(__get_user_32t_8) ENTRY(__get_user_64t_1) check_uaccess r0, 1, r1, r2, __get_user_bad8 @@ -98,6 +103,7 @@ ENTRY(__get_user_64t_1) mov r0, #0 ret lr ENDPROC(__get_user_64t_1) +_ASM_NOKPROBE(__get_user_64t_1) ENTRY(__get_user_64t_2) check_uaccess r0, 2, r1, r2, __get_user_bad8 @@ -114,6 +120,7 @@ rb .req r0 mov r0, #0 ret lr ENDPROC(__get_user_64t_2) +_ASM_NOKPROBE(__get_user_64t_2) ENTRY(__get_user_64t_4) check_uaccess r0, 4, r1, r2, __get_user_bad8 @@ -121,6 +128,7 @@ ENTRY(__get_user_64t_4) mov r0, #0 ret lr ENDPROC(__get_user_64t_4) +_ASM_NOKPROBE(__get_user_64t_4) #endif __get_user_bad8: @@ -131,6 +139,8 @@ __get_user_bad: ret lr ENDPROC(__get_user_bad) ENDPROC(__get_user_bad8) +_ASM_NOKPROBE(__get_user_bad) +_ASM_NOKPROBE(__get_user_bad8) .pushsection __ex_table, "a" .long 1b, __get_user_bad diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index 004f9c8de0329c..d1e8ce7b4bd212 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -205,12 +205,17 @@ static const short da830_evm_mmc_sd_pins[] = { -1 }; +#define DA830_MMCSD_WP_PIN GPIO_TO_PIN(2, 1) +#define DA830_MMCSD_CD_PIN GPIO_TO_PIN(2, 2) + static struct gpiod_lookup_table mmc_gpios_table = { .dev_id = "da830-mmc.0", .table = { /* gpio chip 1 contains gpio range 32-63 */ - GPIO_LOOKUP("davinci_gpio.1", 2, "cd", GPIO_ACTIVE_LOW), - GPIO_LOOKUP("davinci_gpio.1", 1, "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("davinci_gpio.0", DA830_MMCSD_CD_PIN, "cd", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP("davinci_gpio.0", DA830_MMCSD_WP_PIN, "wp", + GPIO_ACTIVE_LOW), }, }; diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 3063478bcc3663..158ed9a1483fc8 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -763,12 +763,17 @@ static const short da850_evm_mcasp_pins[] __initconst = { -1 }; +#define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0) +#define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1) + static struct gpiod_lookup_table mmc_gpios_table = { .dev_id = "da830-mmc.0", .table = { /* gpio chip 2 contains gpio range 64-95 */ - GPIO_LOOKUP("davinci_gpio.2", 0, "cd", GPIO_ACTIVE_LOW), - GPIO_LOOKUP("davinci_gpio.2", 1, "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("davinci_gpio.0", DA850_MMCSD_CD_PIN, "cd", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP("davinci_gpio.0", DA850_MMCSD_WP_PIN, "wp", + GPIO_ACTIVE_LOW), }, }; diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c index cb30637d9eaf8e..23ab9e8bc04c0e 100644 --- a/arch/arm/mach-davinci/board-dm355-evm.c +++ b/arch/arm/mach-davinci/board-dm355-evm.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -109,12 +110,15 @@ static struct platform_device davinci_nand_device = { }, }; +#define DM355_I2C_SDA_PIN GPIO_TO_PIN(0, 15) +#define DM355_I2C_SCL_PIN GPIO_TO_PIN(0, 14) + static struct gpiod_lookup_table i2c_recovery_gpiod_table = { - .dev_id = "i2c_davinci", + .dev_id = "i2c_davinci.1", .table = { - GPIO_LOOKUP("davinci_gpio", 15, "sda", + GPIO_LOOKUP("davinci_gpio.0", DM355_I2C_SDA_PIN, "sda", GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), - GPIO_LOOKUP("davinci_gpio", 14, "scl", + GPIO_LOOKUP("davinci_gpio.0", DM355_I2C_SCL_PIN, "scl", GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), }, }; @@ -179,11 +183,16 @@ static struct resource dm355evm_dm9000_rsrc[] = { }, }; +static struct dm9000_plat_data dm335evm_dm9000_platdata; + static struct platform_device dm355evm_dm9000 = { .name = "dm9000", .id = -1, .resource = dm355evm_dm9000_rsrc, .num_resources = ARRAY_SIZE(dm355evm_dm9000_rsrc), + .dev = { + .platform_data = &dm335evm_dm9000_platdata, + }, }; static struct tvp514x_platform_data tvp5146_pdata = { diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 95b55aae1366f6..509e64ab1994ac 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -596,12 +597,15 @@ static struct i2c_board_info __initdata i2c_info[] = { }, }; +#define DM644X_I2C_SDA_PIN GPIO_TO_PIN(2, 12) +#define DM644X_I2C_SCL_PIN GPIO_TO_PIN(2, 11) + static struct gpiod_lookup_table i2c_recovery_gpiod_table = { - .dev_id = "i2c_davinci", + .dev_id = "i2c_davinci.1", .table = { - GPIO_LOOKUP("davinci_gpio", 44, "sda", + GPIO_LOOKUP("davinci_gpio.0", DM644X_I2C_SDA_PIN, "sda", GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), - GPIO_LOOKUP("davinci_gpio", 43, "scl", + GPIO_LOOKUP("davinci_gpio.0", DM644X_I2C_SCL_PIN, "scl", GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), }, }; diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index 2d37f5b0e1f5ce..a3c0d1e8764784 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -532,11 +532,12 @@ static struct vpif_display_config dm646x_vpif_display_config = { .set_clock = set_vpif_clock, .subdevinfo = dm646x_vpif_subdev, .subdev_count = ARRAY_SIZE(dm646x_vpif_subdev), + .i2c_adapter_id = 1, .chan_config[0] = { .outputs = dm6467_ch0_outputs, .output_count = ARRAY_SIZE(dm6467_ch0_outputs), }, - .card_name = "DM646x EVM", + .card_name = "DM646x EVM Video Display", }; /** @@ -674,6 +675,7 @@ static struct vpif_capture_config dm646x_vpif_capture_cfg = { .setup_input_channel_mode = setup_vpif_input_channel_mode, .subdev_info = vpif_capture_sdev_info, .subdev_count = ARRAY_SIZE(vpif_capture_sdev_info), + .i2c_adapter_id = 1, .chan_config[0] = { .inputs = dm6467_ch0_inputs, .input_count = ARRAY_SIZE(dm6467_ch0_inputs), @@ -694,6 +696,7 @@ static struct vpif_capture_config dm646x_vpif_capture_cfg = { .fid_pol = 0, }, }, + .card_name = "DM646x EVM Video Capture", }; static void __init evm_init_video(void) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 0d32042b728fa4..be8b892a6ea706 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -123,12 +123,16 @@ static const short hawk_mmcsd0_pins[] = { -1 }; +#define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) +#define DA850_HAWK_MMCSD_WP_PIN GPIO_TO_PIN(3, 13) + static struct gpiod_lookup_table mmc_gpios_table = { .dev_id = "da830-mmc.0", .table = { - /* CD: gpio3_12: gpio60: chip 1 contains gpio range 32-63*/ - GPIO_LOOKUP("davinci_gpio.0", 28, "cd", GPIO_ACTIVE_LOW), - GPIO_LOOKUP("davinci_gpio.0", 29, "wp", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("davinci_gpio.0", DA850_HAWK_MMCSD_CD_PIN, "cd", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP("davinci_gpio.0", DA850_HAWK_MMCSD_WP_PIN, "wp", + GPIO_ACTIVE_LOW), }, }; diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 109ab1fa0d2c32..c32ca27ab343d1 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -488,7 +488,8 @@ static u8 dm646x_default_priorities[DAVINCI_N_AINTC_IRQ] = { [IRQ_DM646X_MCASP0TXINT] = 7, [IRQ_DM646X_MCASP0RXINT] = 7, [IRQ_DM646X_RESERVED_3] = 7, - [IRQ_DM646X_MCASP1TXINT] = 7, /* clockevent */ + [IRQ_DM646X_MCASP1TXINT] = 7, + [IRQ_TINT0_TINT12] = 7, /* clockevent */ [IRQ_TINT0_TINT34] = 7, /* clocksource */ [IRQ_TINT1_TINT12] = 7, /* DSP timer */ [IRQ_TINT1_TINT34] = 7, /* system tick */ diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index e70feec6fad5e2..0581ffbedddd37 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -323,7 +323,7 @@ void __init ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr) /* All EP93xx devices use the same two GPIO pins for I2C bit-banging */ static struct gpiod_lookup_table ep93xx_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.0", .table = { /* Use local offsets on gpiochip/port "G" */ GPIO_LOOKUP_IDX("G", 1, NULL, 0, diff --git a/arch/arm/mach-ixp4xx/avila-setup.c b/arch/arm/mach-ixp4xx/avila-setup.c index 77def6169f506a..44cbbce6bda6a8 100644 --- a/arch/arm/mach-ixp4xx/avila-setup.c +++ b/arch/arm/mach-ixp4xx/avila-setup.c @@ -51,7 +51,7 @@ static struct platform_device avila_flash = { }; static struct gpiod_lookup_table avila_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.0", .table = { GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", AVILA_SDA_PIN, NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), diff --git a/arch/arm/mach-ixp4xx/dsmg600-setup.c b/arch/arm/mach-ixp4xx/dsmg600-setup.c index 0f5c99941a7d5b..397190f3a8da6c 100644 --- a/arch/arm/mach-ixp4xx/dsmg600-setup.c +++ b/arch/arm/mach-ixp4xx/dsmg600-setup.c @@ -70,7 +70,7 @@ static struct platform_device dsmg600_flash = { }; static struct gpiod_lookup_table dsmg600_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.0", .table = { GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", DSMG600_SDA_PIN, NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), diff --git a/arch/arm/mach-ixp4xx/fsg-setup.c b/arch/arm/mach-ixp4xx/fsg-setup.c index 033f79b35d5125..f0a152e365b10c 100644 --- a/arch/arm/mach-ixp4xx/fsg-setup.c +++ b/arch/arm/mach-ixp4xx/fsg-setup.c @@ -56,7 +56,7 @@ static struct platform_device fsg_flash = { }; static struct gpiod_lookup_table fsg_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.0", .table = { GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", FSG_SDA_PIN, NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c index b168e2fbdbeb4f..3ec829d52cdd21 100644 --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c +++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -124,7 +124,7 @@ static struct platform_device ixdp425_flash_nand = { #endif /* CONFIG_MTD_NAND_PLATFORM */ static struct gpiod_lookup_table ixdp425_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.0", .table = { GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", IXDP425_SDA_PIN, NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c index 76dfff03cb714e..4138d6aa4c52e6 100644 --- a/arch/arm/mach-ixp4xx/nas100d-setup.c +++ b/arch/arm/mach-ixp4xx/nas100d-setup.c @@ -102,7 +102,7 @@ static struct platform_device nas100d_leds = { }; static struct gpiod_lookup_table nas100d_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.0", .table = { GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", NAS100D_SDA_PIN, NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c index 91da63a7d7b5f9..341b263482ef98 100644 --- a/arch/arm/mach-ixp4xx/nslu2-setup.c +++ b/arch/arm/mach-ixp4xx/nslu2-setup.c @@ -70,7 +70,7 @@ static struct platform_device nslu2_flash = { }; static struct gpiod_lookup_table nslu2_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.0", .table = { GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", NSLU2_SDA_PIN, NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), diff --git a/arch/arm/mach-keystone/pm_domain.c b/arch/arm/mach-keystone/pm_domain.c index fe57e26926292f..abca83d22ff3f1 100644 --- a/arch/arm/mach-keystone/pm_domain.c +++ b/arch/arm/mach-keystone/pm_domain.c @@ -29,6 +29,7 @@ static struct dev_pm_domain keystone_pm_domain = { static struct pm_clk_notifier_block platform_domain_notifier = { .pm_domain = &keystone_pm_domain, + .con_ids = { NULL }, }; static const struct of_device_id of_keystone_table[] = { diff --git a/arch/arm/mach-omap1/ams-delta-fiq.c b/arch/arm/mach-omap1/ams-delta-fiq.c index 793a24a53c5261..d7ca9e2b40d274 100644 --- a/arch/arm/mach-omap1/ams-delta-fiq.c +++ b/arch/arm/mach-omap1/ams-delta-fiq.c @@ -58,22 +58,24 @@ static irqreturn_t deferred_fiq(int irq, void *dev_id) irq_num = gpio_to_irq(gpio); fiq_count = fiq_buffer[FIQ_CNT_INT_00 + gpio]; - while (irq_counter[gpio] < fiq_count) { - if (gpio != AMS_DELTA_GPIO_PIN_KEYBRD_CLK) { - struct irq_data *d = irq_get_irq_data(irq_num); - - /* - * It looks like handle_edge_irq() that - * OMAP GPIO edge interrupts default to, - * expects interrupt already unmasked. - */ - if (irq_chip && irq_chip->irq_unmask) + if (irq_counter[gpio] < fiq_count && + gpio != AMS_DELTA_GPIO_PIN_KEYBRD_CLK) { + struct irq_data *d = irq_get_irq_data(irq_num); + + /* + * handle_simple_irq() that OMAP GPIO edge + * interrupts default to since commit 80ac93c27441 + * requires interrupt already acked and unmasked. + */ + if (irq_chip) { + if (irq_chip->irq_ack) + irq_chip->irq_ack(d); + if (irq_chip->irq_unmask) irq_chip->irq_unmask(d); } - generic_handle_irq(irq_num); - - irq_counter[gpio]++; } + for (; irq_counter[gpio] < fiq_count; irq_counter[gpio]++) + generic_handle_irq(irq_num); } return IRQ_HANDLED; } diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c index 76eb6ec5f157e9..1e6a967cd2d589 100644 --- a/arch/arm/mach-omap2/powerdomain.c +++ b/arch/arm/mach-omap2/powerdomain.c @@ -188,7 +188,7 @@ static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) ((prev & OMAP_POWERSTATE_MASK) << 0)); trace_power_domain_target_rcuidle(pwrdm->name, trace_state, - smp_processor_id()); + raw_smp_processor_id()); } break; default: @@ -518,7 +518,7 @@ int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) if (arch_pwrdm && arch_pwrdm->pwrdm_set_next_pwrst) { /* Trace the pwrdm desired target state */ trace_power_domain_target_rcuidle(pwrdm->name, pwrst, - smp_processor_id()); + raw_smp_processor_id()); /* Program the pwrdm desired target state */ ret = arch_pwrdm->pwrdm_set_next_pwrst(pwrdm, pwrst); } diff --git a/arch/arm/mach-pxa/palmz72.c b/arch/arm/mach-pxa/palmz72.c index 5877e547cecd84..0adb1bd6208e27 100644 --- a/arch/arm/mach-pxa/palmz72.c +++ b/arch/arm/mach-pxa/palmz72.c @@ -322,7 +322,7 @@ static struct soc_camera_link palmz72_iclink = { }; static struct gpiod_lookup_table palmz72_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.0", .table = { GPIO_LOOKUP_IDX("gpio-pxa", 118, NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), diff --git a/arch/arm/mach-pxa/viper.c b/arch/arm/mach-pxa/viper.c index 90d0f277de55a6..207dcc2e94e706 100644 --- a/arch/arm/mach-pxa/viper.c +++ b/arch/arm/mach-pxa/viper.c @@ -460,7 +460,7 @@ static struct platform_device smc91x_device = { /* i2c */ static struct gpiod_lookup_table viper_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.1", .table = { GPIO_LOOKUP_IDX("gpio-pxa", VIPER_RTC_I2C_SDA_GPIO, NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), @@ -789,7 +789,7 @@ static int __init viper_tpm_setup(char *str) __setup("tpm=", viper_tpm_setup); struct gpiod_lookup_table viper_tpm_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.2", .table = { GPIO_LOOKUP_IDX("gpio-pxa", VIPER_TPM_I2C_SDA_GPIO, NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c index ace010479eb6c6..f45aed2519ba21 100644 --- a/arch/arm/mach-sa1100/simpad.c +++ b/arch/arm/mach-sa1100/simpad.c @@ -327,7 +327,7 @@ static struct platform_device simpad_gpio_leds = { * i2c */ static struct gpiod_lookup_table simpad_i2c_gpiod_table = { - .dev_id = "i2c-gpio", + .dev_id = "i2c-gpio.0", .table = { GPIO_LOOKUP_IDX("gpio", 21, NULL, 0, GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN), diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 8c398fedbbb6af..ada8eb206a90b6 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -466,12 +466,6 @@ void __init dma_contiguous_early_fixup(phys_addr_t base, unsigned long size) void __init dma_contiguous_remap(void) { int i; - - if (!dma_mmu_remap_num) - return; - - /* call flush_cache_all() since CMA area would be large enough */ - flush_cache_all(); for (i = 0; i < dma_mmu_remap_num; i++) { phys_addr_t start = dma_mmu_remap[i].base; phys_addr_t end = start + dma_mmu_remap[i].size; @@ -504,15 +498,7 @@ void __init dma_contiguous_remap(void) flush_tlb_kernel_range(__phys_to_virt(start), __phys_to_virt(end)); - /* - * All the memory in CMA region will be on ZONE_MOVABLE. - * If that zone is considered as highmem, the memory in CMA - * region is also considered as highmem even if it's - * physical address belong to lowmem. In this case, - * re-mapping isn't required. - */ - if (!is_highmem_idx(ZONE_MOVABLE)) - iotable_init(&map, 1); + iotable_init(&map, 1); } } diff --git a/arch/arm/probes/kprobes/opt-arm.c b/arch/arm/probes/kprobes/opt-arm.c index bcdecc25461bca..b2aa9b32bff2b5 100644 --- a/arch/arm/probes/kprobes/opt-arm.c +++ b/arch/arm/probes/kprobes/opt-arm.c @@ -165,13 +165,14 @@ optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs) { unsigned long flags; struct kprobe *p = &op->kp; - struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + struct kprobe_ctlblk *kcb; /* Save skipped registers */ regs->ARM_pc = (unsigned long)op->kp.addr; regs->ARM_ORIG_r0 = ~0UL; local_irq_save(flags); + kcb = get_kprobe_ctlblk(); if (kprobe_running()) { kprobes_inc_nmissed_count(&op->kp); @@ -191,6 +192,7 @@ optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs) local_irq_restore(flags); } +NOKPROBE_SYMBOL(optimized_callback) int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *orig) { diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 4c375e11ae9531..af4ee2cef2f965 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -257,7 +257,7 @@ static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_ if (exceptions == VFP_EXCEPTION_ERROR) { vfp_panic("unhandled bounce", inst); - vfp_raise_sigfpe(FPE_FIXME, regs); + vfp_raise_sigfpe(FPE_FLTINV, regs); return; } diff --git a/arch/arm64/boot/dts/exynos/exynos5433.dtsi b/arch/arm64/boot/dts/exynos/exynos5433.dtsi index c0231d077fa61f..1ad8677f6a0a62 100644 --- a/arch/arm64/boot/dts/exynos/exynos5433.dtsi +++ b/arch/arm64/boot/dts/exynos/exynos5433.dtsi @@ -1317,7 +1317,7 @@ reg = <0x14d60000 0x100>; dmas = <&pdma0 31 &pdma0 30>; dma-names = "tx", "rx"; - interrupts = ; + interrupts = ; clocks = <&cmu_peric CLK_PCLK_I2S1>, <&cmu_peric CLK_PCLK_I2S1>, <&cmu_peric CLK_SCLK_I2S1>; diff --git a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts index 724a0d3b76837a..edb4ee0b8896b2 100644 --- a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts +++ b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts @@ -299,7 +299,6 @@ /* GPIO blocks 16 thru 19 do not appear to be routed to pins */ dwmmc_0: dwmmc0@f723d000 { - max-frequency = <150000000>; cap-mmc-highspeed; mmc-hs200-1_8v; non-removable; diff --git a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi index 48cad7919efa38..ed2f1237ea1e9a 100644 --- a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi +++ b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi @@ -38,9 +38,10 @@ compatible = "marvell,armada-7k-pp22"; reg = <0x0 0x100000>, <0x129000 0xb000>; clocks = <&CP110_LABEL(clk) 1 3>, <&CP110_LABEL(clk) 1 9>, - <&CP110_LABEL(clk) 1 5>, <&CP110_LABEL(clk) 1 18>; + <&CP110_LABEL(clk) 1 5>, <&CP110_LABEL(clk) 1 6>, + <&CP110_LABEL(clk) 1 18>; clock-names = "pp_clk", "gop_clk", - "mg_clk", "axi_clk"; + "mg_clk", "mg_core_clk", "axi_clk"; marvell,system-controller = <&CP110_LABEL(syscon0)>; status = "disabled"; dma-coherent; @@ -141,6 +142,8 @@ #size-cells = <0>; compatible = "marvell,xmdio"; reg = <0x12a600 0x10>; + clocks = <&CP110_LABEL(clk) 1 5>, + <&CP110_LABEL(clk) 1 6>, <&CP110_LABEL(clk) 1 18>; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi b/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi index a8baad7b80df2d..13f57fff147742 100644 --- a/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi @@ -46,7 +46,7 @@ compatible = "ethernet-phy-ieee802.3-c22"; reg = <0x0>; interrupt-parent = <&gpio>; - interrupts = ; + interrupts = ; }; }; }; diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi b/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi index e62bda1cf2d9db..c32dd3419c870e 100644 --- a/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi +++ b/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi @@ -414,7 +414,7 @@ mmc-ddr-1_8v; mmc-hs200-1_8v; mmc-pwrseq = <&emmc_pwrseq>; - cdns,phy-input-delay-legacy = <4>; + cdns,phy-input-delay-legacy = <9>; cdns,phy-input-delay-mmc-highspeed = <2>; cdns,phy-input-delay-mmc-ddr = <3>; cdns,phy-dll-delay-sdclk = <21>; diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld20-ref.dts b/arch/arm64/boot/dts/socionext/uniphier-ld20-ref.dts index 2c1a92fafbfbe0..440c2e6a638b99 100644 --- a/arch/arm64/boot/dts/socionext/uniphier-ld20-ref.dts +++ b/arch/arm64/boot/dts/socionext/uniphier-ld20-ref.dts @@ -67,3 +67,11 @@ reg = <0>; }; }; + +&pinctrl_ether_rgmii { + tx { + pins = "RGMII_TXCLK", "RGMII_TXD0", "RGMII_TXD1", + "RGMII_TXD2", "RGMII_TXD3", "RGMII_TXCTL"; + drive-strength = <9>; + }; +}; diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi b/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi index 9efe20d075890e..3a5ed789c056e3 100644 --- a/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi +++ b/arch/arm64/boot/dts/socionext/uniphier-ld20.dtsi @@ -519,7 +519,7 @@ mmc-ddr-1_8v; mmc-hs200-1_8v; mmc-pwrseq = <&emmc_pwrseq>; - cdns,phy-input-delay-legacy = <4>; + cdns,phy-input-delay-legacy = <9>; cdns,phy-input-delay-mmc-highspeed = <2>; cdns,phy-input-delay-mmc-ddr = <3>; cdns,phy-dll-delay-sdclk = <21>; diff --git a/arch/arm64/boot/dts/socionext/uniphier-pxs3.dtsi b/arch/arm64/boot/dts/socionext/uniphier-pxs3.dtsi index 7c8f710d9bfa11..e85d6ddea3c217 100644 --- a/arch/arm64/boot/dts/socionext/uniphier-pxs3.dtsi +++ b/arch/arm64/boot/dts/socionext/uniphier-pxs3.dtsi @@ -334,7 +334,7 @@ mmc-ddr-1_8v; mmc-hs200-1_8v; mmc-pwrseq = <&emmc_pwrseq>; - cdns,phy-input-delay-legacy = <4>; + cdns,phy-input-delay-legacy = <9>; cdns,phy-input-delay-mmc-highspeed = <2>; cdns,phy-input-delay-mmc-ddr = <3>; cdns,phy-dll-delay-sdclk = <21>; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index ecf613761e78ee..fe005df02ed324 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -320,6 +320,7 @@ CONFIG_PINCTRL_MAX77620=y CONFIG_PINCTRL_MSM8916=y CONFIG_PINCTRL_MSM8994=y CONFIG_PINCTRL_MSM8996=y +CONFIG_PINCTRL_MT7622=y CONFIG_PINCTRL_QDF2XXX=y CONFIG_PINCTRL_QCOM_SPMI_PMIC=y CONFIG_GPIO_DWAPB=y diff --git a/arch/arm64/include/asm/atomic_lse.h b/arch/arm64/include/asm/atomic_lse.h index 9ef0797380cbbd..f9b0b09153e0ea 100644 --- a/arch/arm64/include/asm/atomic_lse.h +++ b/arch/arm64/include/asm/atomic_lse.h @@ -117,7 +117,7 @@ static inline void atomic_and(int i, atomic_t *v) /* LSE atomics */ " mvn %w[i], %w[i]\n" " stclr %w[i], %[v]") - : [i] "+r" (w0), [v] "+Q" (v->counter) + : [i] "+&r" (w0), [v] "+Q" (v->counter) : "r" (x1) : __LL_SC_CLOBBERS); } @@ -135,7 +135,7 @@ static inline int atomic_fetch_and##name(int i, atomic_t *v) \ /* LSE atomics */ \ " mvn %w[i], %w[i]\n" \ " ldclr" #mb " %w[i], %w[i], %[v]") \ - : [i] "+r" (w0), [v] "+Q" (v->counter) \ + : [i] "+&r" (w0), [v] "+Q" (v->counter) \ : "r" (x1) \ : __LL_SC_CLOBBERS, ##cl); \ \ @@ -161,7 +161,7 @@ static inline void atomic_sub(int i, atomic_t *v) /* LSE atomics */ " neg %w[i], %w[i]\n" " stadd %w[i], %[v]") - : [i] "+r" (w0), [v] "+Q" (v->counter) + : [i] "+&r" (w0), [v] "+Q" (v->counter) : "r" (x1) : __LL_SC_CLOBBERS); } @@ -180,7 +180,7 @@ static inline int atomic_sub_return##name(int i, atomic_t *v) \ " neg %w[i], %w[i]\n" \ " ldadd" #mb " %w[i], w30, %[v]\n" \ " add %w[i], %w[i], w30") \ - : [i] "+r" (w0), [v] "+Q" (v->counter) \ + : [i] "+&r" (w0), [v] "+Q" (v->counter) \ : "r" (x1) \ : __LL_SC_CLOBBERS , ##cl); \ \ @@ -207,7 +207,7 @@ static inline int atomic_fetch_sub##name(int i, atomic_t *v) \ /* LSE atomics */ \ " neg %w[i], %w[i]\n" \ " ldadd" #mb " %w[i], %w[i], %[v]") \ - : [i] "+r" (w0), [v] "+Q" (v->counter) \ + : [i] "+&r" (w0), [v] "+Q" (v->counter) \ : "r" (x1) \ : __LL_SC_CLOBBERS, ##cl); \ \ @@ -314,7 +314,7 @@ static inline void atomic64_and(long i, atomic64_t *v) /* LSE atomics */ " mvn %[i], %[i]\n" " stclr %[i], %[v]") - : [i] "+r" (x0), [v] "+Q" (v->counter) + : [i] "+&r" (x0), [v] "+Q" (v->counter) : "r" (x1) : __LL_SC_CLOBBERS); } @@ -332,7 +332,7 @@ static inline long atomic64_fetch_and##name(long i, atomic64_t *v) \ /* LSE atomics */ \ " mvn %[i], %[i]\n" \ " ldclr" #mb " %[i], %[i], %[v]") \ - : [i] "+r" (x0), [v] "+Q" (v->counter) \ + : [i] "+&r" (x0), [v] "+Q" (v->counter) \ : "r" (x1) \ : __LL_SC_CLOBBERS, ##cl); \ \ @@ -358,7 +358,7 @@ static inline void atomic64_sub(long i, atomic64_t *v) /* LSE atomics */ " neg %[i], %[i]\n" " stadd %[i], %[v]") - : [i] "+r" (x0), [v] "+Q" (v->counter) + : [i] "+&r" (x0), [v] "+Q" (v->counter) : "r" (x1) : __LL_SC_CLOBBERS); } @@ -377,7 +377,7 @@ static inline long atomic64_sub_return##name(long i, atomic64_t *v) \ " neg %[i], %[i]\n" \ " ldadd" #mb " %[i], x30, %[v]\n" \ " add %[i], %[i], x30") \ - : [i] "+r" (x0), [v] "+Q" (v->counter) \ + : [i] "+&r" (x0), [v] "+Q" (v->counter) \ : "r" (x1) \ : __LL_SC_CLOBBERS, ##cl); \ \ @@ -404,7 +404,7 @@ static inline long atomic64_fetch_sub##name(long i, atomic64_t *v) \ /* LSE atomics */ \ " neg %[i], %[i]\n" \ " ldadd" #mb " %[i], %[i], %[v]") \ - : [i] "+r" (x0), [v] "+Q" (v->counter) \ + : [i] "+&r" (x0), [v] "+Q" (v->counter) \ : "r" (x1) \ : __LL_SC_CLOBBERS, ##cl); \ \ @@ -435,7 +435,7 @@ static inline long atomic64_dec_if_positive(atomic64_t *v) " sub x30, x30, %[ret]\n" " cbnz x30, 1b\n" "2:") - : [ret] "+r" (x0), [v] "+Q" (v->counter) + : [ret] "+&r" (x0), [v] "+Q" (v->counter) : : __LL_SC_CLOBBERS, "cc", "memory"); @@ -516,7 +516,7 @@ static inline long __cmpxchg_double##name(unsigned long old1, \ " eor %[old1], %[old1], %[oldval1]\n" \ " eor %[old2], %[old2], %[oldval2]\n" \ " orr %[old1], %[old1], %[old2]") \ - : [old1] "+r" (x0), [old2] "+r" (x1), \ + : [old1] "+&r" (x0), [old2] "+&r" (x1), \ [v] "+Q" (*(unsigned long *)ptr) \ : [new1] "r" (x2), [new2] "r" (x3), [ptr] "r" (x4), \ [oldval1] "r" (oldval1), [oldval2] "r" (oldval2) \ diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index 30014a9f8f2b33..ea690b3562afb2 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -75,6 +75,7 @@ #define ARM_CPU_IMP_CAVIUM 0x43 #define ARM_CPU_IMP_BRCM 0x42 #define ARM_CPU_IMP_QCOM 0x51 +#define ARM_CPU_IMP_NVIDIA 0x4E #define ARM_CPU_PART_AEM_V8 0xD0F #define ARM_CPU_PART_FOUNDATION 0xD00 @@ -99,6 +100,9 @@ #define QCOM_CPU_PART_FALKOR 0xC00 #define QCOM_CPU_PART_KRYO 0x200 +#define NVIDIA_CPU_PART_DENVER 0x003 +#define NVIDIA_CPU_PART_CARMEL 0x004 + #define MIDR_CORTEX_A53 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) #define MIDR_CORTEX_A57 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57) #define MIDR_CORTEX_A72 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A72) @@ -114,6 +118,8 @@ #define MIDR_QCOM_FALKOR_V1 MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_FALKOR_V1) #define MIDR_QCOM_FALKOR MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_FALKOR) #define MIDR_QCOM_KRYO MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO) +#define MIDR_NVIDIA_DENVER MIDR_CPU_MODEL(ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_DENVER) +#define MIDR_NVIDIA_CARMEL MIDR_CPU_MODEL(ARM_CPU_IMP_NVIDIA, NVIDIA_CPU_PART_CARMEL) #ifndef __ASSEMBLY__ diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index 082110993647b9..6128992c2ded6b 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -360,6 +360,22 @@ static inline unsigned int kvm_get_vmid_bits(void) return (cpuid_feature_extract_unsigned_field(reg, ID_AA64MMFR1_VMIDBITS_SHIFT) == 2) ? 16 : 8; } +/* + * We are not in the kvm->srcu critical section most of the time, so we take + * the SRCU read lock here. Since we copy the data from the user page, we + * can immediately drop the lock again. + */ +static inline int kvm_read_guest_lock(struct kvm *kvm, + gpa_t gpa, void *data, unsigned long len) +{ + int srcu_idx = srcu_read_lock(&kvm->srcu); + int ret = kvm_read_guest(kvm, gpa, data, len); + + srcu_read_unlock(&kvm->srcu, srcu_idx); + + return ret; +} + #ifdef CONFIG_KVM_INDIRECT_VECTORS /* * EL2 vectors can be mapped and rerouted in a number of ways, diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 66be504edb6cf5..d894a20b70b28f 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -75,3 +75,11 @@ NOKPROBE_SYMBOL(_mcount); /* arm-smccc */ EXPORT_SYMBOL(__arm_smccc_smc); EXPORT_SYMBOL(__arm_smccc_hvc); + + /* tishift.S */ +extern long long __ashlti3(long long a, int b); +EXPORT_SYMBOL(__ashlti3); +extern long long __ashrti3(long long a, int b); +EXPORT_SYMBOL(__ashrti3); +extern long long __lshrti3(long long a, int b); +EXPORT_SYMBOL(__lshrti3); diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index a900befadfe84a..e4a1182deff7f2 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -316,6 +316,7 @@ static const struct midr_range arm64_bp_harden_smccc_cpus[] = { MIDR_ALL_VERSIONS(MIDR_CAVIUM_THUNDERX2), MIDR_ALL_VERSIONS(MIDR_QCOM_FALKOR_V1), MIDR_ALL_VERSIONS(MIDR_QCOM_FALKOR), + MIDR_ALL_VERSIONS(MIDR_NVIDIA_DENVER), {}, }; diff --git a/arch/arm64/lib/tishift.S b/arch/arm64/lib/tishift.S index d3db9b2cd479be..0fdff97794debb 100644 --- a/arch/arm64/lib/tishift.S +++ b/arch/arm64/lib/tishift.S @@ -1,17 +1,6 @@ -/* - * Copyright (C) 2017 Jason A. Donenfeld . All Rights Reserved. +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * Copyright (C) 2017-2018 Jason A. Donenfeld . All Rights Reserved. */ #include diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 4165485e8b6ecb..2af3dd89bcdbed 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -293,6 +293,57 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr, static void __do_user_fault(struct siginfo *info, unsigned int esr) { current->thread.fault_address = (unsigned long)info->si_addr; + + /* + * If the faulting address is in the kernel, we must sanitize the ESR. + * From userspace's point of view, kernel-only mappings don't exist + * at all, so we report them as level 0 translation faults. + * (This is not quite the way that "no mapping there at all" behaves: + * an alignment fault not caused by the memory type would take + * precedence over translation fault for a real access to empty + * space. Unfortunately we can't easily distinguish "alignment fault + * not caused by memory type" from "alignment fault caused by memory + * type", so we ignore this wrinkle and just return the translation + * fault.) + */ + if (current->thread.fault_address >= TASK_SIZE) { + switch (ESR_ELx_EC(esr)) { + case ESR_ELx_EC_DABT_LOW: + /* + * These bits provide only information about the + * faulting instruction, which userspace knows already. + * We explicitly clear bits which are architecturally + * RES0 in case they are given meanings in future. + * We always report the ESR as if the fault was taken + * to EL1 and so ISV and the bits in ISS[23:14] are + * clear. (In fact it always will be a fault to EL1.) + */ + esr &= ESR_ELx_EC_MASK | ESR_ELx_IL | + ESR_ELx_CM | ESR_ELx_WNR; + esr |= ESR_ELx_FSC_FAULT; + break; + case ESR_ELx_EC_IABT_LOW: + /* + * Claim a level 0 translation fault. + * All other bits are architecturally RES0 for faults + * reported with that DFSC value, so we clear them. + */ + esr &= ESR_ELx_EC_MASK | ESR_ELx_IL; + esr |= ESR_ELx_FSC_FAULT; + break; + default: + /* + * This should never happen (entry.S only brings us + * into this code for insn and data aborts from a lower + * exception level). Fail safe by not providing an ESR + * context record at all. + */ + WARN(1, "ESR 0x%x is not DABT or IABT from EL0\n", esr); + esr = 0; + break; + } + } + current->thread.fault_code = esr; arm64_force_sig_info(info, esr_to_fault_info(esr)->name, current); } diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 9f3c47acf8ffb1..1b18b472242034 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -646,8 +646,10 @@ static int keep_initrd __initdata; void __init free_initrd_mem(unsigned long start, unsigned long end) { - if (!keep_initrd) + if (!keep_initrd) { free_reserved_area((void *)start, (void *)end, 0, "initrd"); + memblock_free(__virt_to_phys(start), end - start); + } } static int __init keepinitrd_setup(char *__unused) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 2dbb2c9f1ec177..493ff75670ffd9 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -933,13 +933,15 @@ int pud_set_huge(pud_t *pudp, phys_addr_t phys, pgprot_t prot) { pgprot_t sect_prot = __pgprot(PUD_TYPE_SECT | pgprot_val(mk_sect_prot(prot))); + pud_t new_pud = pfn_pud(__phys_to_pfn(phys), sect_prot); - /* ioremap_page_range doesn't honour BBM */ - if (pud_present(READ_ONCE(*pudp))) + /* Only allow permission changes for now */ + if (!pgattr_change_is_safe(READ_ONCE(pud_val(*pudp)), + pud_val(new_pud))) return 0; BUG_ON(phys & ~PUD_MASK); - set_pud(pudp, pfn_pud(__phys_to_pfn(phys), sect_prot)); + set_pud(pudp, new_pud); return 1; } @@ -947,13 +949,15 @@ int pmd_set_huge(pmd_t *pmdp, phys_addr_t phys, pgprot_t prot) { pgprot_t sect_prot = __pgprot(PMD_TYPE_SECT | pgprot_val(mk_sect_prot(prot))); + pmd_t new_pmd = pfn_pmd(__phys_to_pfn(phys), sect_prot); - /* ioremap_page_range doesn't honour BBM */ - if (pmd_present(READ_ONCE(*pmdp))) + /* Only allow permission changes for now */ + if (!pgattr_change_is_safe(READ_ONCE(pmd_val(*pmdp)), + pmd_val(new_pmd))) return 0; BUG_ON(phys & ~PMD_MASK); - set_pmd(pmdp, pfn_pmd(__phys_to_pfn(phys), sect_prot)); + set_pmd(pmdp, new_pmd); return 1; } diff --git a/arch/mips/boot/compressed/uart-16550.c b/arch/mips/boot/compressed/uart-16550.c index b3043c08f76942..aee8d7b8f09143 100644 --- a/arch/mips/boot/compressed/uart-16550.c +++ b/arch/mips/boot/compressed/uart-16550.c @@ -18,9 +18,9 @@ #define PORT(offset) (CKSEG1ADDR(AR7_REGS_UART0) + (4 * offset)) #endif -#if defined(CONFIG_MACH_JZ4740) || defined(CONFIG_MACH_JZ4780) -#include -#define PORT(offset) (CKSEG1ADDR(JZ4740_UART0_BASE_ADDR) + (4 * offset)) +#ifdef CONFIG_MACH_INGENIC +#define INGENIC_UART0_BASE_ADDR 0x10030000 +#define PORT(offset) (CKSEG1ADDR(INGENIC_UART0_BASE_ADDR) + (4 * offset)) #endif #ifdef CONFIG_CPU_XLR diff --git a/arch/mips/boot/dts/xilfpga/Makefile b/arch/mips/boot/dts/xilfpga/Makefile index 9987e0e378c50c..69ca00590b8de6 100644 --- a/arch/mips/boot/dts/xilfpga/Makefile +++ b/arch/mips/boot/dts/xilfpga/Makefile @@ -1,4 +1,2 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_FIT_IMAGE_FDT_XILFPGA) += nexys4ddr.dtb - -obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) diff --git a/arch/mips/generic/Platform b/arch/mips/generic/Platform index b51432dd10b6fa..0dd0d5d460a5fc 100644 --- a/arch/mips/generic/Platform +++ b/arch/mips/generic/Platform @@ -16,3 +16,4 @@ all-$(CONFIG_MIPS_GENERIC) := vmlinux.gz.itb its-y := vmlinux.its.S its-$(CONFIG_FIT_IMAGE_FDT_BOSTON) += board-boston.its.S its-$(CONFIG_FIT_IMAGE_FDT_NI169445) += board-ni169445.its.S +its-$(CONFIG_FIT_IMAGE_FDT_XILFPGA) += board-xilfpga.its.S diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index b9e9bf6288497b..3775a8d694fb08 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -721,6 +721,10 @@ int mips_set_process_fp_mode(struct task_struct *task, unsigned int value) if (value & ~known_bits) return -EOPNOTSUPP; + /* Setting FRE without FR is not supported. */ + if ((value & (PR_FP_MODE_FR | PR_FP_MODE_FRE)) == PR_FP_MODE_FRE) + return -EOPNOTSUPP; + /* Avoid inadvertently triggering emulation */ if ((value & PR_FP_MODE_FR) && raw_cpu_has_fpu && !(raw_current_cpu_data.fpu_id & MIPS_FPIR_F64)) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 0b23b1ad99e65f..0c0c23c9c9f5a7 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -463,7 +463,7 @@ static int fpr_get_msa(struct task_struct *target, /* * Copy the floating-point context to the supplied NT_PRFPREG buffer. * Choose the appropriate helper for general registers, and then copy - * the FCSR register separately. + * the FCSR and FIR registers separately. */ static int fpr_get(struct task_struct *target, const struct user_regset *regset, @@ -471,6 +471,7 @@ static int fpr_get(struct task_struct *target, void *kbuf, void __user *ubuf) { const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); + const int fir_pos = fcr31_pos + sizeof(u32); int err; if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) @@ -483,6 +484,12 @@ static int fpr_get(struct task_struct *target, err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.fpu.fcr31, fcr31_pos, fcr31_pos + sizeof(u32)); + if (err) + return err; + + err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &boot_cpu_data.fpu_id, + fir_pos, fir_pos + sizeof(u32)); return err; } @@ -531,7 +538,8 @@ static int fpr_set_msa(struct task_struct *target, /* * Copy the supplied NT_PRFPREG buffer to the floating-point context. * Choose the appropriate helper for general registers, and then copy - * the FCSR register separately. + * the FCSR register separately. Ignore the incoming FIR register + * contents though, as the register is read-only. * * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0', * which is supposed to have been guaranteed by the kernel before @@ -545,6 +553,7 @@ static int fpr_set(struct task_struct *target, const void *kbuf, const void __user *ubuf) { const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); + const int fir_pos = fcr31_pos + sizeof(u32); u32 fcr31; int err; @@ -572,6 +581,11 @@ static int fpr_set(struct task_struct *target, ptrace_setfcr31(target, fcr31); } + if (count > 0) + err = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + fir_pos, + fir_pos + sizeof(u32)); + return err; } @@ -793,7 +807,7 @@ long arch_ptrace(struct task_struct *child, long request, fregs = get_fpu_regs(child); #ifdef CONFIG_32BIT - if (test_thread_flag(TIF_32BIT_FPREGS)) { + if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { /* * The odd registers are actually the high * order bits of the values stored in the even @@ -804,7 +818,7 @@ long arch_ptrace(struct task_struct *child, long request, break; } #endif - tmp = get_fpr32(&fregs[addr - FPR_BASE], 0); + tmp = get_fpr64(&fregs[addr - FPR_BASE], 0); break; case PC: tmp = regs->cp0_epc; @@ -888,7 +902,7 @@ long arch_ptrace(struct task_struct *child, long request, init_fp_ctx(child); #ifdef CONFIG_32BIT - if (test_thread_flag(TIF_32BIT_FPREGS)) { + if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { /* * The odd registers are actually the high * order bits of the values stored in the even diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index 2b9260f92ccd30..f30c381d3e1ced 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c @@ -99,7 +99,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; } fregs = get_fpu_regs(child); - if (test_thread_flag(TIF_32BIT_FPREGS)) { + if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { /* * The odd registers are actually the high * order bits of the values stored in the even @@ -109,7 +109,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, addr & 1); break; } - tmp = get_fpr32(&fregs[addr - FPR_BASE], 0); + tmp = get_fpr64(&fregs[addr - FPR_BASE], 0); break; case PC: tmp = regs->cp0_epc; @@ -212,7 +212,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, sizeof(child->thread.fpu)); child->thread.fpu.fcr31 = 0; } - if (test_thread_flag(TIF_32BIT_FPREGS)) { + if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { /* * The odd registers are actually the high * order bits of the values stored in the even diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c index 2549fdd27ee168..0f725e9cee8f69 100644 --- a/arch/mips/kvm/mips.c +++ b/arch/mips/kvm/mips.c @@ -45,7 +45,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { { "cache", VCPU_STAT(cache_exits), KVM_STAT_VCPU }, { "signal", VCPU_STAT(signal_exits), KVM_STAT_VCPU }, { "interrupt", VCPU_STAT(int_exits), KVM_STAT_VCPU }, - { "cop_unsuable", VCPU_STAT(cop_unusable_exits), KVM_STAT_VCPU }, + { "cop_unusable", VCPU_STAT(cop_unusable_exits), KVM_STAT_VCPU }, { "tlbmod", VCPU_STAT(tlbmod_exits), KVM_STAT_VCPU }, { "tlbmiss_ld", VCPU_STAT(tlbmiss_ld_exits), KVM_STAT_VCPU }, { "tlbmiss_st", VCPU_STAT(tlbmiss_st_exits), KVM_STAT_VCPU }, diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index 6f534b2099717d..e12dfa48b478dd 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -851,9 +851,12 @@ static void r4k_dma_cache_wback_inv(unsigned long addr, unsigned long size) /* * Either no secondary cache or the available caches don't have the * subset property so we have to flush the primary caches - * explicitly + * explicitly. + * If we would need IPI to perform an INDEX-type operation, then + * we have to use the HIT-type alternative as IPI cannot be used + * here due to interrupts possibly being disabled. */ - if (size >= dcache_size) { + if (!r4k_op_needs_ipi(R4K_INDEX) && size >= dcache_size) { r4k_blast_dcache(); } else { R4600_HIT_CACHEOP_WAR_IMPL; @@ -890,7 +893,7 @@ static void r4k_dma_cache_inv(unsigned long addr, unsigned long size) return; } - if (size >= dcache_size) { + if (!r4k_op_needs_ipi(R4K_INDEX) && size >= dcache_size) { r4k_blast_dcache(); } else { R4600_HIT_CACHEOP_WAR_IMPL; diff --git a/arch/nds32/Kconfig b/arch/nds32/Kconfig index 249f38d3388f20..b7404f2dcf5bf7 100644 --- a/arch/nds32/Kconfig +++ b/arch/nds32/Kconfig @@ -9,6 +9,12 @@ config NDS32 select CLKSRC_MMIO select CLONE_BACKWARDS select COMMON_CLK + select GENERIC_ASHLDI3 + select GENERIC_ASHRDI3 + select GENERIC_LSHRDI3 + select GENERIC_CMPDI2 + select GENERIC_MULDI3 + select GENERIC_UCMPDI2 select GENERIC_ATOMIC64 select GENERIC_CPU_DEVICES select GENERIC_CLOCKEVENTS @@ -82,6 +88,7 @@ endmenu menu "Kernel Features" source "kernel/Kconfig.preempt" +source "kernel/Kconfig.freezer" source "mm/Kconfig" source "kernel/Kconfig.hz" endmenu diff --git a/arch/nds32/Kconfig.cpu b/arch/nds32/Kconfig.cpu index ba44cc539da9a0..b8c8984d145616 100644 --- a/arch/nds32/Kconfig.cpu +++ b/arch/nds32/Kconfig.cpu @@ -1,10 +1,11 @@ comment "Processor Features" config CPU_BIG_ENDIAN - bool "Big endian" + def_bool !CPU_LITTLE_ENDIAN config CPU_LITTLE_ENDIAN - def_bool !CPU_BIG_ENDIAN + bool "Little endian" + default y config HWZOL bool "hardware zero overhead loop support" diff --git a/arch/nds32/Makefile b/arch/nds32/Makefile index 91f933d5a962ae..513bb2e9baf9fa 100644 --- a/arch/nds32/Makefile +++ b/arch/nds32/Makefile @@ -23,9 +23,6 @@ export TEXTADDR # If we have a machine-specific directory, then include it in the build. core-y += arch/nds32/kernel/ arch/nds32/mm/ libs-y += arch/nds32/lib/ -LIBGCC_PATH := \ - $(shell $(CC) $(KBUILD_CFLAGS) $(KCFLAGS) -print-libgcc-file-name) -libs-y += $(LIBGCC_PATH) ifneq '$(CONFIG_NDS32_BUILTIN_DTB)' '""' BUILTIN_DTB := y @@ -35,8 +32,12 @@ endif ifdef CONFIG_CPU_LITTLE_ENDIAN KBUILD_CFLAGS += $(call cc-option, -EL) +KBUILD_AFLAGS += $(call cc-option, -EL) +LDFLAGS += $(call cc-option, -EL) else KBUILD_CFLAGS += $(call cc-option, -EB) +KBUILD_AFLAGS += $(call cc-option, -EB) +LDFLAGS += $(call cc-option, -EB) endif boot := arch/nds32/boot diff --git a/arch/nds32/include/asm/Kbuild b/arch/nds32/include/asm/Kbuild index 06bdf8167f5a15..142e612aa63987 100644 --- a/arch/nds32/include/asm/Kbuild +++ b/arch/nds32/include/asm/Kbuild @@ -16,6 +16,7 @@ generic-y += dma.h generic-y += emergency-restart.h generic-y += errno.h generic-y += exec.h +generic-y += export.h generic-y += fb.h generic-y += fcntl.h generic-y += ftrace.h @@ -49,6 +50,7 @@ generic-y += switch_to.h generic-y += timex.h generic-y += topology.h generic-y += trace_clock.h +generic-y += xor.h generic-y += unaligned.h generic-y += user.h generic-y += vga.h diff --git a/arch/nds32/include/asm/bitfield.h b/arch/nds32/include/asm/bitfield.h index c73f71d6774450..8e84fc385b946c 100644 --- a/arch/nds32/include/asm/bitfield.h +++ b/arch/nds32/include/asm/bitfield.h @@ -336,7 +336,7 @@ #define INT_MASK_mskIDIVZE ( 0x1 << INT_MASK_offIDIVZE ) #define INT_MASK_mskDSSIM ( 0x1 << INT_MASK_offDSSIM ) -#define INT_MASK_INITAIAL_VAL 0x10003 +#define INT_MASK_INITAIAL_VAL (INT_MASK_mskDSSIM|INT_MASK_mskIDIVZE) /****************************************************************************** * ir15: INT_PEND (Interrupt Pending Register) @@ -396,6 +396,7 @@ #define MMU_CTL_D8KB 1 #define MMU_CTL_UNA ( 0x1 << MMU_CTL_offUNA ) +#define MMU_CTL_CACHEABLE_NON 0 #define MMU_CTL_CACHEABLE_WB 2 #define MMU_CTL_CACHEABLE_WT 3 diff --git a/arch/nds32/include/asm/cacheflush.h b/arch/nds32/include/asm/cacheflush.h index 1240f148ec0f34..10b48f0d8e857f 100644 --- a/arch/nds32/include/asm/cacheflush.h +++ b/arch/nds32/include/asm/cacheflush.h @@ -32,6 +32,8 @@ void flush_anon_page(struct vm_area_struct *vma, #define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE void flush_kernel_dcache_page(struct page *page); +void flush_kernel_vmap_range(void *addr, int size); +void invalidate_kernel_vmap_range(void *addr, int size); void flush_icache_range(unsigned long start, unsigned long end); void flush_icache_page(struct vm_area_struct *vma, struct page *page); #define flush_dcache_mmap_lock(mapping) xa_lock_irq(&(mapping)->i_pages) diff --git a/arch/nds32/include/asm/io.h b/arch/nds32/include/asm/io.h index 966e71b3c960bf..71cd226d6863ee 100644 --- a/arch/nds32/include/asm/io.h +++ b/arch/nds32/include/asm/io.h @@ -4,6 +4,8 @@ #ifndef __ASM_NDS32_IO_H #define __ASM_NDS32_IO_H +#include + extern void iounmap(volatile void __iomem *addr); #define __raw_writeb __raw_writeb static inline void __raw_writeb(u8 val, volatile void __iomem *addr) diff --git a/arch/nds32/include/asm/page.h b/arch/nds32/include/asm/page.h index e27365c097b60e..947f0491c9a717 100644 --- a/arch/nds32/include/asm/page.h +++ b/arch/nds32/include/asm/page.h @@ -27,6 +27,9 @@ extern void copy_user_highpage(struct page *to, struct page *from, unsigned long vaddr, struct vm_area_struct *vma); extern void clear_user_highpage(struct page *page, unsigned long vaddr); +void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, + struct page *to); +void clear_user_page(void *addr, unsigned long vaddr, struct page *page); #define __HAVE_ARCH_COPY_USER_HIGHPAGE #define clear_user_highpage clear_user_highpage #else diff --git a/arch/nds32/include/asm/pgtable.h b/arch/nds32/include/asm/pgtable.h index 6783937edbeba2..d3e19a55cf5300 100644 --- a/arch/nds32/include/asm/pgtable.h +++ b/arch/nds32/include/asm/pgtable.h @@ -152,6 +152,7 @@ extern void __pgd_error(const char *file, int line, unsigned long val); #define PAGE_CACHE_L1 __pgprot(_HAVE_PAGE_L | _PAGE_V | _PAGE_M_KRW | _PAGE_D | _PAGE_E | _PAGE_G | _PAGE_CACHE) #define PAGE_MEMORY __pgprot(_HAVE_PAGE_L | _PAGE_V | _PAGE_M_KRW | _PAGE_D | _PAGE_E | _PAGE_G | _PAGE_CACHE_SHRD) #define PAGE_KERNEL __pgprot(_PAGE_V | _PAGE_M_KRW | _PAGE_D | _PAGE_E | _PAGE_G | _PAGE_CACHE_SHRD) +#define PAGE_SHARED __pgprot(_PAGE_V | _PAGE_M_URW_KRW | _PAGE_D | _PAGE_CACHE_SHRD) #define PAGE_DEVICE __pgprot(_PAGE_V | _PAGE_M_KRW | _PAGE_D | _PAGE_G | _PAGE_C_DEV) #endif /* __ASSEMBLY__ */ diff --git a/arch/nds32/kernel/ex-entry.S b/arch/nds32/kernel/ex-entry.S index a72e83d804f5e0..b8ae4e9a6b93db 100644 --- a/arch/nds32/kernel/ex-entry.S +++ b/arch/nds32/kernel/ex-entry.S @@ -118,7 +118,7 @@ common_exception_handler: /* interrupt */ 2: #ifdef CONFIG_TRACE_IRQFLAGS - jal arch_trace_hardirqs_off + jal trace_hardirqs_off #endif move $r0, $sp sethi $lp, hi20(ret_from_intr) diff --git a/arch/nds32/kernel/head.S b/arch/nds32/kernel/head.S index 71f57bd70f3b8c..c5fdae174ced5a 100644 --- a/arch/nds32/kernel/head.S +++ b/arch/nds32/kernel/head.S @@ -57,14 +57,32 @@ _nodtb: isb mtsr $r4, $L1_PPTB ! load page table pointer\n" -/* set NTC0 cacheable/writeback, mutliple page size in use */ +#ifdef CONFIG_CPU_DCACHE_DISABLE + #define MMU_CTL_NTCC MMU_CTL_CACHEABLE_NON +#else + #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + #define MMU_CTL_NTCC MMU_CTL_CACHEABLE_WT + #else + #define MMU_CTL_NTCC MMU_CTL_CACHEABLE_WB + #endif +#endif + +/* set NTC cacheability, mutliple page size in use */ mfsr $r3, $MMU_CTL - li $r0, #~MMU_CTL_mskNTC0 - and $r3, $r3, $r0 +#if CONFIG_MEMORY_START >= 0xc0000000 + ori $r3, $r3, (MMU_CTL_NTCC << MMU_CTL_offNTC3) +#elif CONFIG_MEMORY_START >= 0x80000000 + ori $r3, $r3, (MMU_CTL_NTCC << MMU_CTL_offNTC2) +#elif CONFIG_MEMORY_START >= 0x40000000 + ori $r3, $r3, (MMU_CTL_NTCC << MMU_CTL_offNTC1) +#else + ori $r3, $r3, (MMU_CTL_NTCC << MMU_CTL_offNTC0) +#endif + #ifdef CONFIG_ANDES_PAGE_SIZE_4KB - ori $r3, $r3, #(MMU_CTL_mskMPZIU|(MMU_CTL_CACHEABLE_WB << MMU_CTL_offNTC0)) + ori $r3, $r3, #(MMU_CTL_mskMPZIU) #else - ori $r3, $r3, #(MMU_CTL_mskMPZIU|(MMU_CTL_CACHEABLE_WB << MMU_CTL_offNTC0)|MMU_CTL_D8KB) + ori $r3, $r3, #(MMU_CTL_mskMPZIU|MMU_CTL_D8KB) #endif #ifdef CONFIG_HW_SUPPORT_UNALIGNMENT_ACCESS li $r0, #MMU_CTL_UNA diff --git a/arch/nds32/kernel/setup.c b/arch/nds32/kernel/setup.c index ba910e9e4ecbf5..2f5b2ccebe4716 100644 --- a/arch/nds32/kernel/setup.c +++ b/arch/nds32/kernel/setup.c @@ -293,6 +293,9 @@ void __init setup_arch(char **cmdline_p) /* paging_init() sets up the MMU and marks all pages as reserved */ paging_init(); + /* invalidate all TLB entries because the new mapping is created */ + __nds32__tlbop_flua(); + /* use generic way to parse */ parse_early_param(); diff --git a/arch/nds32/kernel/stacktrace.c b/arch/nds32/kernel/stacktrace.c index bc70113c0e8446..8b231e910ea689 100644 --- a/arch/nds32/kernel/stacktrace.c +++ b/arch/nds32/kernel/stacktrace.c @@ -9,6 +9,7 @@ void save_stack_trace(struct stack_trace *trace) { save_stack_trace_tsk(current, trace); } +EXPORT_SYMBOL_GPL(save_stack_trace); void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) { @@ -45,3 +46,4 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) fpn = (unsigned long *)fpp; } } +EXPORT_SYMBOL_GPL(save_stack_trace_tsk); diff --git a/arch/nds32/kernel/vdso.c b/arch/nds32/kernel/vdso.c index f1198d7a565455..016f15891f6d40 100644 --- a/arch/nds32/kernel/vdso.c +++ b/arch/nds32/kernel/vdso.c @@ -23,7 +23,7 @@ #include #include extern struct cache_info L1_cache_info[2]; -extern char vdso_start, vdso_end; +extern char vdso_start[], vdso_end[]; static unsigned long vdso_pages __ro_after_init; static unsigned long timer_mapping_base; @@ -66,16 +66,16 @@ static int __init vdso_init(void) int i; struct page **vdso_pagelist; - if (memcmp(&vdso_start, "\177ELF", 4)) { + if (memcmp(vdso_start, "\177ELF", 4)) { pr_err("vDSO is not a valid ELF object!\n"); return -EINVAL; } /* Creat a timer io mapping to get clock cycles counter */ get_timer_node_info(); - vdso_pages = (&vdso_end - &vdso_start) >> PAGE_SHIFT; + vdso_pages = (vdso_end - vdso_start) >> PAGE_SHIFT; pr_info("vdso: %ld pages (%ld code @ %p, %ld data @ %p)\n", - vdso_pages + 1, vdso_pages, &vdso_start, 1L, vdso_data); + vdso_pages + 1, vdso_pages, vdso_start, 1L, vdso_data); /* Allocate the vDSO pagelist */ vdso_pagelist = kcalloc(vdso_pages, sizeof(struct page *), GFP_KERNEL); @@ -83,7 +83,7 @@ static int __init vdso_init(void) return -ENOMEM; for (i = 0; i < vdso_pages; i++) - vdso_pagelist[i] = virt_to_page(&vdso_start + i * PAGE_SIZE); + vdso_pagelist[i] = virt_to_page(vdso_start + i * PAGE_SIZE); vdso_spec[1].pages = &vdso_pagelist[0]; return 0; diff --git a/arch/nds32/lib/copy_page.S b/arch/nds32/lib/copy_page.S index 4a2ff85f17ee81..f8701ed161a8ba 100644 --- a/arch/nds32/lib/copy_page.S +++ b/arch/nds32/lib/copy_page.S @@ -2,6 +2,7 @@ // Copyright (C) 2005-2017 Andes Technology Corporation #include +#include #include .text @@ -16,6 +17,7 @@ ENTRY(copy_page) popm $r2, $r10 ret ENDPROC(copy_page) +EXPORT_SYMBOL(copy_page) ENTRY(clear_page) pushm $r1, $r9 @@ -35,3 +37,4 @@ ENTRY(clear_page) popm $r1, $r9 ret ENDPROC(clear_page) +EXPORT_SYMBOL(clear_page) diff --git a/arch/nds32/mm/alignment.c b/arch/nds32/mm/alignment.c index b96a01b10ca7fc..e1aed9dc692dd3 100644 --- a/arch/nds32/mm/alignment.c +++ b/arch/nds32/mm/alignment.c @@ -19,7 +19,7 @@ #define RA(inst) (((inst) >> 15) & 0x1FUL) #define RB(inst) (((inst) >> 10) & 0x1FUL) #define SV(inst) (((inst) >> 8) & 0x3UL) -#define IMM(inst) (((inst) >> 0) & 0x3FFFUL) +#define IMM(inst) (((inst) >> 0) & 0x7FFFUL) #define RA3(inst) (((inst) >> 3) & 0x7UL) #define RT3(inst) (((inst) >> 6) & 0x7UL) @@ -28,6 +28,9 @@ #define RA5(inst) (((inst) >> 0) & 0x1FUL) #define RT4(inst) (((inst) >> 5) & 0xFUL) +#define GET_IMMSVAL(imm_value) \ + (((imm_value >> 14) & 0x1) ? (imm_value - 0x8000) : imm_value) + #define __get8_data(val,addr,err) \ __asm__( \ "1: lbi.bi %1, [%2], #1\n" \ @@ -467,7 +470,7 @@ static inline int do_32(unsigned long inst, struct pt_regs *regs) } if (imm) - shift = IMM(inst) * len; + shift = GET_IMMSVAL(IMM(inst)) * len; else shift = *idx_to_addr(regs, RB(inst)) << SV(inst); @@ -552,7 +555,7 @@ static struct ctl_table alignment_tbl[3] = { static struct ctl_table nds32_sysctl_table[2] = { { - .procname = "unaligned_acess", + .procname = "unaligned_access", .mode = 0555, .child = alignment_tbl}, {} diff --git a/arch/nds32/mm/cacheflush.c b/arch/nds32/mm/cacheflush.c index 6eb786a399a2f6..ce8fd34497bf04 100644 --- a/arch/nds32/mm/cacheflush.c +++ b/arch/nds32/mm/cacheflush.c @@ -147,6 +147,25 @@ void flush_cache_vunmap(unsigned long start, unsigned long end) cpu_icache_inval_all(); } +void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, + struct page *to) +{ + cpu_dcache_wbinval_page((unsigned long)vaddr); + cpu_icache_inval_page((unsigned long)vaddr); + copy_page(vto, vfrom); + cpu_dcache_wbinval_page((unsigned long)vto); + cpu_icache_inval_page((unsigned long)vto); +} + +void clear_user_page(void *addr, unsigned long vaddr, struct page *page) +{ + cpu_dcache_wbinval_page((unsigned long)vaddr); + cpu_icache_inval_page((unsigned long)vaddr); + clear_page(addr); + cpu_dcache_wbinval_page((unsigned long)addr); + cpu_icache_inval_page((unsigned long)addr); +} + void copy_user_highpage(struct page *to, struct page *from, unsigned long vaddr, struct vm_area_struct *vma) { @@ -156,11 +175,9 @@ void copy_user_highpage(struct page *to, struct page *from, pto = page_to_phys(to); pfrom = page_to_phys(from); + local_irq_save(flags); if (aliasing(vaddr, (unsigned long)kfrom)) cpu_dcache_wb_page((unsigned long)kfrom); - if (aliasing(vaddr, (unsigned long)kto)) - cpu_dcache_inval_page((unsigned long)kto); - local_irq_save(flags); vto = kremap0(vaddr, pto); vfrom = kremap1(vaddr, pfrom); copy_page((void *)vto, (void *)vfrom); @@ -198,21 +215,25 @@ void flush_dcache_page(struct page *page) if (mapping && !mapping_mapped(mapping)) set_bit(PG_dcache_dirty, &page->flags); else { - int i, pc; - unsigned long vto, kaddr, flags; + unsigned long kaddr, flags; + kaddr = (unsigned long)page_address(page); - cpu_dcache_wbinval_page(kaddr); - pc = CACHE_SET(DCACHE) * CACHE_LINE_SIZE(DCACHE) / PAGE_SIZE; local_irq_save(flags); - for (i = 0; i < pc; i++) { - vto = - kremap0(kaddr + i * PAGE_SIZE, page_to_phys(page)); - cpu_dcache_wbinval_page(vto); - kunmap01(vto); + cpu_dcache_wbinval_page(kaddr); + if (mapping) { + unsigned long vaddr, kto; + + vaddr = page->index << PAGE_SHIFT; + if (aliasing(vaddr, kaddr)) { + kto = kremap0(vaddr, page_to_phys(page)); + cpu_dcache_wbinval_page(kto); + kunmap01(kto); + } } local_irq_restore(flags); } } +EXPORT_SYMBOL(flush_dcache_page); void copy_to_user_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr, void *dst, void *src, int len) @@ -251,7 +272,7 @@ void copy_from_user_page(struct vm_area_struct *vma, struct page *page, void flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vaddr) { - unsigned long flags; + unsigned long kaddr, flags, ktmp; if (!PageAnon(page)) return; @@ -261,7 +282,12 @@ void flush_anon_page(struct vm_area_struct *vma, local_irq_save(flags); if (vma->vm_flags & VM_EXEC) cpu_icache_inval_page(vaddr & PAGE_MASK); - cpu_dcache_wbinval_page((unsigned long)page_address(page)); + kaddr = (unsigned long)page_address(page); + if (aliasing(vaddr, kaddr)) { + ktmp = kremap0(vaddr, page_to_phys(page)); + cpu_dcache_wbinval_page(ktmp); + kunmap01(ktmp); + } local_irq_restore(flags); } @@ -272,6 +298,25 @@ void flush_kernel_dcache_page(struct page *page) cpu_dcache_wbinval_page((unsigned long)page_address(page)); local_irq_restore(flags); } +EXPORT_SYMBOL(flush_kernel_dcache_page); + +void flush_kernel_vmap_range(void *addr, int size) +{ + unsigned long flags; + local_irq_save(flags); + cpu_dcache_wb_range((unsigned long)addr, (unsigned long)addr + size); + local_irq_restore(flags); +} +EXPORT_SYMBOL(flush_kernel_vmap_range); + +void invalidate_kernel_vmap_range(void *addr, int size) +{ + unsigned long flags; + local_irq_save(flags); + cpu_dcache_inval_range((unsigned long)addr, (unsigned long)addr + size); + local_irq_restore(flags); +} +EXPORT_SYMBOL(invalidate_kernel_vmap_range); void flush_icache_range(unsigned long start, unsigned long end) { @@ -283,6 +328,7 @@ void flush_icache_range(unsigned long start, unsigned long end) cpu_cache_wbinval_range(start, end, 1); local_irq_restore(flags); } +EXPORT_SYMBOL(flush_icache_range); void flush_icache_page(struct vm_area_struct *vma, struct page *page) { diff --git a/arch/nds32/mm/init.c b/arch/nds32/mm/init.c index 93ee0160720bfd..c713d2ad55dc9a 100644 --- a/arch/nds32/mm/init.c +++ b/arch/nds32/mm/init.c @@ -30,6 +30,7 @@ extern unsigned long phys_initrd_size; * zero-initialized data and COW. */ struct page *empty_zero_page; +EXPORT_SYMBOL(empty_zero_page); static void __init zone_sizes_init(void) { diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c index ee5a78a151a6ac..e0e1c9775c320b 100644 --- a/arch/parisc/kernel/drivers.c +++ b/arch/parisc/kernel/drivers.c @@ -268,7 +268,7 @@ static struct parisc_device *find_device_by_addr(unsigned long hpa) * Walks up the device tree looking for a device of the specified type. * If it finds it, it returns it. If not, it returns NULL. */ -const struct parisc_device * __init +const struct parisc_device * find_pa_parent_type(const struct parisc_device *padev, int type) { const struct device *dev = &padev->dev; diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c index 4065b5e48c9d68..5e26dbede5fc23 100644 --- a/arch/parisc/kernel/smp.c +++ b/arch/parisc/kernel/smp.c @@ -423,8 +423,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle) } #ifdef CONFIG_PROC_FS -int __init -setup_profiling_timer(unsigned int multiplier) +int setup_profiling_timer(unsigned int multiplier) { return -EINVAL; } diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 471b2274fbeba8..c40b4380951cb4 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -74,6 +74,27 @@ */ #define EX_R3 EX_DAR +#define STF_ENTRY_BARRIER_SLOT \ + STF_ENTRY_BARRIER_FIXUP_SECTION; \ + nop; \ + nop; \ + nop + +#define STF_EXIT_BARRIER_SLOT \ + STF_EXIT_BARRIER_FIXUP_SECTION; \ + nop; \ + nop; \ + nop; \ + nop; \ + nop; \ + nop + +/* + * r10 must be free to use, r13 must be paca + */ +#define INTERRUPT_TO_KERNEL \ + STF_ENTRY_BARRIER_SLOT + /* * Macros for annotating the expected destination of (h)rfid * @@ -90,16 +111,19 @@ rfid #define RFI_TO_USER \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ rfid; \ b rfi_flush_fallback #define RFI_TO_USER_OR_KERNEL \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ rfid; \ b rfi_flush_fallback #define RFI_TO_GUEST \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ rfid; \ b rfi_flush_fallback @@ -108,21 +132,25 @@ hrfid #define HRFI_TO_USER \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ hrfid; \ b hrfi_flush_fallback #define HRFI_TO_USER_OR_KERNEL \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ hrfid; \ b hrfi_flush_fallback #define HRFI_TO_GUEST \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ hrfid; \ b hrfi_flush_fallback #define HRFI_TO_UNKNOWN \ + STF_EXIT_BARRIER_SLOT; \ RFI_FLUSH_SLOT; \ hrfid; \ b hrfi_flush_fallback @@ -254,6 +282,7 @@ END_FTR_SECTION_NESTED(ftr,ftr,943) #define __EXCEPTION_PROLOG_1_PRE(area) \ OPT_SAVE_REG_TO_PACA(area+EX_PPR, r9, CPU_FTR_HAS_PPR); \ OPT_SAVE_REG_TO_PACA(area+EX_CFAR, r10, CPU_FTR_CFAR); \ + INTERRUPT_TO_KERNEL; \ SAVE_CTR(r10, area); \ mfcr r9; diff --git a/arch/powerpc/include/asm/feature-fixups.h b/arch/powerpc/include/asm/feature-fixups.h index 1e82eb3caabd19..a9b64df34e2a36 100644 --- a/arch/powerpc/include/asm/feature-fixups.h +++ b/arch/powerpc/include/asm/feature-fixups.h @@ -187,6 +187,22 @@ label##3: \ FTR_ENTRY_OFFSET label##1b-label##3b; \ .popsection; +#define STF_ENTRY_BARRIER_FIXUP_SECTION \ +953: \ + .pushsection __stf_entry_barrier_fixup,"a"; \ + .align 2; \ +954: \ + FTR_ENTRY_OFFSET 953b-954b; \ + .popsection; + +#define STF_EXIT_BARRIER_FIXUP_SECTION \ +955: \ + .pushsection __stf_exit_barrier_fixup,"a"; \ + .align 2; \ +956: \ + FTR_ENTRY_OFFSET 955b-956b; \ + .popsection; + #define RFI_FLUSH_FIXUP_SECTION \ 951: \ .pushsection __rfi_flush_fixup,"a"; \ @@ -199,6 +215,9 @@ label##3: \ #ifndef __ASSEMBLY__ #include +extern long stf_barrier_fallback; +extern long __start___stf_entry_barrier_fixup, __stop___stf_entry_barrier_fixup; +extern long __start___stf_exit_barrier_fixup, __stop___stf_exit_barrier_fixup; extern long __start___rfi_flush_fixup, __stop___rfi_flush_fixup; void apply_feature_fixups(void); diff --git a/arch/powerpc/include/asm/ftrace.h b/arch/powerpc/include/asm/ftrace.h index 9abddde372abfb..b2dabd06659dd0 100644 --- a/arch/powerpc/include/asm/ftrace.h +++ b/arch/powerpc/include/asm/ftrace.h @@ -69,17 +69,30 @@ struct dyn_arch_ftrace { #endif #if defined(CONFIG_FTRACE_SYSCALLS) && !defined(__ASSEMBLY__) -#ifdef PPC64_ELF_ABI_v1 +/* + * Some syscall entry functions on powerpc start with "ppc_" (fork and clone, + * for instance) or ppc32_/ppc64_. We should also match the sys_ variant with + * those. + */ #define ARCH_HAS_SYSCALL_MATCH_SYM_NAME +#ifdef PPC64_ELF_ABI_v1 +static inline bool arch_syscall_match_sym_name(const char *sym, const char *name) +{ + /* We need to skip past the initial dot, and the __se_sys alias */ + return !strcmp(sym + 1, name) || + (!strncmp(sym, ".__se_sys", 9) && !strcmp(sym + 6, name)) || + (!strncmp(sym, ".ppc_", 5) && !strcmp(sym + 5, name + 4)) || + (!strncmp(sym, ".ppc32_", 7) && !strcmp(sym + 7, name + 4)) || + (!strncmp(sym, ".ppc64_", 7) && !strcmp(sym + 7, name + 4)); +} +#else static inline bool arch_syscall_match_sym_name(const char *sym, const char *name) { - /* - * Compare the symbol name with the system call name. Skip the .sys or .SyS - * prefix from the symbol name and the sys prefix from the system call name and - * just match the rest. This is only needed on ppc64 since symbol names on - * 32bit do not start with a period so the generic function will work. - */ - return !strcmp(sym + 4, name + 3); + return !strcmp(sym, name) || + (!strncmp(sym, "__se_sys", 8) && !strcmp(sym + 5, name)) || + (!strncmp(sym, "ppc_", 4) && !strcmp(sym + 4, name + 4)) || + (!strncmp(sym, "ppc32_", 6) && !strcmp(sym + 6, name + 4)) || + (!strncmp(sym, "ppc64_", 6) && !strcmp(sym + 6, name + 4)); } #endif #endif /* CONFIG_FTRACE_SYSCALLS && !__ASSEMBLY__ */ diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h index 4c02a7378d067e..e7377b73cfecaa 100644 --- a/arch/powerpc/include/asm/kvm_book3s.h +++ b/arch/powerpc/include/asm/kvm_book3s.h @@ -96,6 +96,7 @@ struct kvmppc_vcore { struct kvm_vcpu *runner; struct kvm *kvm; u64 tb_offset; /* guest timebase - host timebase */ + u64 tb_offset_applied; /* timebase offset currently in force */ ulong lpcr; u32 arch_compat; ulong pcr; diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h index 4185f1c9612501..3f109a3e3edb22 100644 --- a/arch/powerpc/include/asm/paca.h +++ b/arch/powerpc/include/asm/paca.h @@ -165,7 +165,6 @@ struct paca_struct { u64 saved_msr; /* MSR saved here by enter_rtas */ u16 trap_save; /* Used when bad stack is encountered */ u8 irq_soft_mask; /* mask for irq soft masking */ - u8 soft_enabled; /* irq soft-enable flag */ u8 irq_happened; /* irq happened while soft-disabled */ u8 io_sync; /* writel() needs spin_unlock sync */ u8 irq_work_pending; /* IRQ_WORK interrupt while soft-disable */ diff --git a/arch/powerpc/include/asm/security_features.h b/arch/powerpc/include/asm/security_features.h index fa4d2e1cf772c8..44989b22383c24 100644 --- a/arch/powerpc/include/asm/security_features.h +++ b/arch/powerpc/include/asm/security_features.h @@ -12,6 +12,17 @@ extern unsigned long powerpc_security_features; extern bool rfi_flush; +/* These are bit flags */ +enum stf_barrier_type { + STF_BARRIER_NONE = 0x1, + STF_BARRIER_FALLBACK = 0x2, + STF_BARRIER_EIEIO = 0x4, + STF_BARRIER_SYNC_ORI = 0x8, +}; + +void setup_stf_barrier(void); +void do_stf_barrier_fixups(enum stf_barrier_type types); + static inline void security_ftr_set(unsigned long feature) { powerpc_security_features |= feature; diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 9f421641a35c82..16b077801a5f97 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -91,6 +91,7 @@ extern int start_topology_update(void); extern int stop_topology_update(void); extern int prrn_is_enabled(void); extern int find_and_online_cpu_nid(int cpu); +extern int timed_topology_update(int nsecs); #else static inline int start_topology_update(void) { @@ -108,16 +109,12 @@ static inline int find_and_online_cpu_nid(int cpu) { return 0; } +static inline int timed_topology_update(int nsecs) +{ + return 0; +} #endif /* CONFIG_NUMA && CONFIG_PPC_SPLPAR */ -#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_NEED_MULTIPLE_NODES) -#if defined(CONFIG_PPC_SPLPAR) -extern int timed_topology_update(int nsecs); -#else -#define timed_topology_update(nsecs) -#endif /* CONFIG_PPC_SPLPAR */ -#endif /* CONFIG_HOTPLUG_CPU || CONFIG_NEED_MULTIPLE_NODES */ - #include #ifdef CONFIG_SMP diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 6bee65f3cfd34b..373dc1d6ef44e9 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -562,6 +562,7 @@ int main(void) OFFSET(VCORE_NAPPING_THREADS, kvmppc_vcore, napping_threads); OFFSET(VCORE_KVM, kvmppc_vcore, kvm); OFFSET(VCORE_TB_OFFSET, kvmppc_vcore, tb_offset); + OFFSET(VCORE_TB_OFFSET_APPL, kvmppc_vcore, tb_offset_applied); OFFSET(VCORE_LPCR, kvmppc_vcore, lpcr); OFFSET(VCORE_PCR, kvmppc_vcore, pcr); OFFSET(VCORE_DPDES, kvmppc_vcore, dpdes); diff --git a/arch/powerpc/kernel/cpu_setup_power.S b/arch/powerpc/kernel/cpu_setup_power.S index 3f30c994e9316a..458b928dbd8447 100644 --- a/arch/powerpc/kernel/cpu_setup_power.S +++ b/arch/powerpc/kernel/cpu_setup_power.S @@ -28,6 +28,7 @@ _GLOBAL(__setup_cpu_power7) beqlr li r0,0 mtspr SPRN_LPID,r0 + mtspr SPRN_PCR,r0 mfspr r3,SPRN_LPCR li r4,(LPCR_LPES1 >> LPCR_LPES_SH) bl __init_LPCR_ISA206 @@ -41,6 +42,7 @@ _GLOBAL(__restore_cpu_power7) beqlr li r0,0 mtspr SPRN_LPID,r0 + mtspr SPRN_PCR,r0 mfspr r3,SPRN_LPCR li r4,(LPCR_LPES1 >> LPCR_LPES_SH) bl __init_LPCR_ISA206 @@ -57,6 +59,7 @@ _GLOBAL(__setup_cpu_power8) beqlr li r0,0 mtspr SPRN_LPID,r0 + mtspr SPRN_PCR,r0 mfspr r3,SPRN_LPCR ori r3, r3, LPCR_PECEDH li r4,0 /* LPES = 0 */ @@ -78,6 +81,7 @@ _GLOBAL(__restore_cpu_power8) beqlr li r0,0 mtspr SPRN_LPID,r0 + mtspr SPRN_PCR,r0 mfspr r3,SPRN_LPCR ori r3, r3, LPCR_PECEDH li r4,0 /* LPES = 0 */ @@ -99,6 +103,7 @@ _GLOBAL(__setup_cpu_power9) mtspr SPRN_PSSCR,r0 mtspr SPRN_LPID,r0 mtspr SPRN_PID,r0 + mtspr SPRN_PCR,r0 mfspr r3,SPRN_LPCR LOAD_REG_IMMEDIATE(r4, LPCR_PECEDH | LPCR_PECE_HVEE | LPCR_HVICE | LPCR_HEIC) or r3, r3, r4 @@ -123,6 +128,7 @@ _GLOBAL(__restore_cpu_power9) mtspr SPRN_PSSCR,r0 mtspr SPRN_LPID,r0 mtspr SPRN_PID,r0 + mtspr SPRN_PCR,r0 mfspr r3,SPRN_LPCR LOAD_REG_IMMEDIATE(r4, LPCR_PECEDH | LPCR_PECE_HVEE | LPCR_HVICE | LPCR_HEIC) or r3, r3, r4 diff --git a/arch/powerpc/kernel/dt_cpu_ftrs.c b/arch/powerpc/kernel/dt_cpu_ftrs.c index 8ab51f6ca03af5..c904477abaf38d 100644 --- a/arch/powerpc/kernel/dt_cpu_ftrs.c +++ b/arch/powerpc/kernel/dt_cpu_ftrs.c @@ -101,6 +101,7 @@ static void __restore_cpu_cpufeatures(void) if (hv_mode) { mtspr(SPRN_LPID, 0); mtspr(SPRN_HFSCR, system_registers.hfscr); + mtspr(SPRN_PCR, 0); } mtspr(SPRN_FSCR, system_registers.fscr); diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index ae6a849db60b1a..f283958129f271 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -885,7 +885,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_TM) #endif -EXC_REAL_MASKABLE(decrementer, 0x900, 0x80, IRQS_DISABLED) +EXC_REAL_OOL_MASKABLE(decrementer, 0x900, 0x80, IRQS_DISABLED) EXC_VIRT_MASKABLE(decrementer, 0x4900, 0x80, 0x900, IRQS_DISABLED) TRAMP_KVM(PACA_EXGEN, 0x900) EXC_COMMON_ASYNC(decrementer_common, 0x900, timer_interrupt) @@ -961,6 +961,7 @@ EXC_COMMON(trap_0b_common, 0xb00, unknown_exception) mtctr r13; \ GET_PACA(r13); \ std r10,PACA_EXGEN+EX_R10(r13); \ + INTERRUPT_TO_KERNEL; \ KVMTEST_PR(0xc00); /* uses r10, branch to do_kvm_0xc00_system_call */ \ HMT_MEDIUM; \ mfctr r9; @@ -969,7 +970,8 @@ EXC_COMMON(trap_0b_common, 0xb00, unknown_exception) #define SYSCALL_KVMTEST \ HMT_MEDIUM; \ mr r9,r13; \ - GET_PACA(r13); + GET_PACA(r13); \ + INTERRUPT_TO_KERNEL; #endif #define LOAD_SYSCALL_HANDLER(reg) \ @@ -1507,6 +1509,19 @@ masked_##_H##interrupt: \ b .; \ MASKED_DEC_HANDLER(_H) +TRAMP_REAL_BEGIN(stf_barrier_fallback) + std r9,PACA_EXRFI+EX_R9(r13) + std r10,PACA_EXRFI+EX_R10(r13) + sync + ld r9,PACA_EXRFI+EX_R9(r13) + ld r10,PACA_EXRFI+EX_R10(r13) + ori 31,31,0 + .rept 14 + b 1f +1: + .endr + blr + TRAMP_REAL_BEGIN(rfi_flush_fallback) SET_SCRATCH0(r13); GET_PACA(r13); diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c index bab5a27ea8056c..b98a722da9151b 100644 --- a/arch/powerpc/kernel/security.c +++ b/arch/powerpc/kernel/security.c @@ -8,6 +8,7 @@ #include #include +#include #include @@ -86,3 +87,151 @@ ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, c return s.len; } + +/* + * Store-forwarding barrier support. + */ + +static enum stf_barrier_type stf_enabled_flush_types; +static bool no_stf_barrier; +bool stf_barrier; + +static int __init handle_no_stf_barrier(char *p) +{ + pr_info("stf-barrier: disabled on command line."); + no_stf_barrier = true; + return 0; +} + +early_param("no_stf_barrier", handle_no_stf_barrier); + +/* This is the generic flag used by other architectures */ +static int __init handle_ssbd(char *p) +{ + if (!p || strncmp(p, "auto", 5) == 0 || strncmp(p, "on", 2) == 0 ) { + /* Until firmware tells us, we have the barrier with auto */ + return 0; + } else if (strncmp(p, "off", 3) == 0) { + handle_no_stf_barrier(NULL); + return 0; + } else + return 1; + + return 0; +} +early_param("spec_store_bypass_disable", handle_ssbd); + +/* This is the generic flag used by other architectures */ +static int __init handle_no_ssbd(char *p) +{ + handle_no_stf_barrier(NULL); + return 0; +} +early_param("nospec_store_bypass_disable", handle_no_ssbd); + +static void stf_barrier_enable(bool enable) +{ + if (enable) + do_stf_barrier_fixups(stf_enabled_flush_types); + else + do_stf_barrier_fixups(STF_BARRIER_NONE); + + stf_barrier = enable; +} + +void setup_stf_barrier(void) +{ + enum stf_barrier_type type; + bool enable, hv; + + hv = cpu_has_feature(CPU_FTR_HVMODE); + + /* Default to fallback in case fw-features are not available */ + if (cpu_has_feature(CPU_FTR_ARCH_300)) + type = STF_BARRIER_EIEIO; + else if (cpu_has_feature(CPU_FTR_ARCH_207S)) + type = STF_BARRIER_SYNC_ORI; + else if (cpu_has_feature(CPU_FTR_ARCH_206)) + type = STF_BARRIER_FALLBACK; + else + type = STF_BARRIER_NONE; + + enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && + (security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR) || + (security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && hv)); + + if (type == STF_BARRIER_FALLBACK) { + pr_info("stf-barrier: fallback barrier available\n"); + } else if (type == STF_BARRIER_SYNC_ORI) { + pr_info("stf-barrier: hwsync barrier available\n"); + } else if (type == STF_BARRIER_EIEIO) { + pr_info("stf-barrier: eieio barrier available\n"); + } + + stf_enabled_flush_types = type; + + if (!no_stf_barrier) + stf_barrier_enable(enable); +} + +ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf) +{ + if (stf_barrier && stf_enabled_flush_types != STF_BARRIER_NONE) { + const char *type; + switch (stf_enabled_flush_types) { + case STF_BARRIER_EIEIO: + type = "eieio"; + break; + case STF_BARRIER_SYNC_ORI: + type = "hwsync"; + break; + case STF_BARRIER_FALLBACK: + type = "fallback"; + break; + default: + type = "unknown"; + } + return sprintf(buf, "Mitigation: Kernel entry/exit barrier (%s)\n", type); + } + + if (!security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV) && + !security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)) + return sprintf(buf, "Not affected\n"); + + return sprintf(buf, "Vulnerable\n"); +} + +#ifdef CONFIG_DEBUG_FS +static int stf_barrier_set(void *data, u64 val) +{ + bool enable; + + if (val == 1) + enable = true; + else if (val == 0) + enable = false; + else + return -EINVAL; + + /* Only do anything if we're changing state */ + if (enable != stf_barrier) + stf_barrier_enable(enable); + + return 0; +} + +static int stf_barrier_get(void *data, u64 *val) +{ + *val = stf_barrier ? 1 : 0; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n"); + +static __init int stf_barrier_debugfs_init(void) +{ + debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier); + return 0; +} +device_initcall(stf_barrier_debugfs_init); +#endif /* CONFIG_DEBUG_FS */ diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index c8af90ff49f052..b8d82678f8b41b 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S @@ -133,6 +133,20 @@ SECTIONS RO_DATA(PAGE_SIZE) #ifdef CONFIG_PPC64 + . = ALIGN(8); + __stf_entry_barrier_fixup : AT(ADDR(__stf_entry_barrier_fixup) - LOAD_OFFSET) { + __start___stf_entry_barrier_fixup = .; + *(__stf_entry_barrier_fixup) + __stop___stf_entry_barrier_fixup = .; + } + + . = ALIGN(8); + __stf_exit_barrier_fixup : AT(ADDR(__stf_exit_barrier_fixup) - LOAD_OFFSET) { + __start___stf_exit_barrier_fixup = .; + *(__stf_exit_barrier_fixup) + __stop___stf_exit_barrier_fixup = .; + } + . = ALIGN(8); __rfi_flush_fixup : AT(ADDR(__rfi_flush_fixup) - LOAD_OFFSET) { __start___rfi_flush_fixup = .; diff --git a/arch/powerpc/kvm/book3s_64_mmu_radix.c b/arch/powerpc/kvm/book3s_64_mmu_radix.c index a57eafec4dc2ee..361f42c8c73e02 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_radix.c +++ b/arch/powerpc/kvm/book3s_64_mmu_radix.c @@ -162,7 +162,7 @@ static void kvmppc_radix_tlbie_page(struct kvm *kvm, unsigned long addr, if (cpu_has_feature(CPU_FTR_P9_TLBIE_BUG)) asm volatile(PPC_TLBIE_5(%0, %1, 0, 0, 1) : : "r" (addr), "r" (kvm->arch.lpid) : "memory"); - asm volatile("ptesync": : :"memory"); + asm volatile("eieio ; tlbsync ; ptesync": : :"memory"); } static void kvmppc_radix_flush_pwc(struct kvm *kvm, unsigned long addr) @@ -173,7 +173,7 @@ static void kvmppc_radix_flush_pwc(struct kvm *kvm, unsigned long addr) /* RIC=1 PRS=0 R=1 IS=2 */ asm volatile(PPC_TLBIE_5(%0, %1, 1, 0, 1) : : "r" (rb), "r" (kvm->arch.lpid) : "memory"); - asm volatile("ptesync": : :"memory"); + asm volatile("eieio ; tlbsync ; ptesync": : :"memory"); } unsigned long kvmppc_radix_update_pte(struct kvm *kvm, pte_t *ptep, @@ -584,7 +584,7 @@ int kvm_unmap_radix(struct kvm *kvm, struct kvm_memory_slot *memslot, ptep = __find_linux_pte(kvm->arch.pgtable, gpa, NULL, &shift); if (ptep && pte_present(*ptep)) { - old = kvmppc_radix_update_pte(kvm, ptep, _PAGE_PRESENT, 0, + old = kvmppc_radix_update_pte(kvm, ptep, ~0UL, 0, gpa, shift); kvmppc_radix_tlbie_page(kvm, gpa, shift); if ((old & _PAGE_DIRTY) && memslot->dirty_bitmap) { diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 4d07fca5121c56..9963f65c212b8c 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -2441,6 +2441,7 @@ static void init_vcore_to_run(struct kvmppc_vcore *vc) vc->in_guest = 0; vc->napping_threads = 0; vc->conferring_threads = 0; + vc->tb_offset_applied = 0; } static bool can_dynamic_split(struct kvmppc_vcore *vc, struct core_info *cip) diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index bd63fa8a08b5dd..07ca1b2a7966b5 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -692,6 +692,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300) 22: ld r8,VCORE_TB_OFFSET(r5) cmpdi r8,0 beq 37f + std r8, VCORE_TB_OFFSET_APPL(r5) mftb r6 /* current host timebase */ add r8,r8,r6 mtspr SPRN_TBU40,r8 /* update upper 40 bits */ @@ -940,18 +941,6 @@ FTR_SECTION_ELSE ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_300) 8: - /* - * Set the decrementer to the guest decrementer. - */ - ld r8,VCPU_DEC_EXPIRES(r4) - /* r8 is a host timebase value here, convert to guest TB */ - ld r5,HSTATE_KVM_VCORE(r13) - ld r6,VCORE_TB_OFFSET(r5) - add r8,r8,r6 - mftb r7 - subf r3,r7,r8 - mtspr SPRN_DEC,r3 - ld r5, VCPU_SPRG0(r4) ld r6, VCPU_SPRG1(r4) ld r7, VCPU_SPRG2(r4) @@ -1005,6 +994,18 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_300) mtspr SPRN_LPCR,r8 isync + /* + * Set the decrementer to the guest decrementer. + */ + ld r8,VCPU_DEC_EXPIRES(r4) + /* r8 is a host timebase value here, convert to guest TB */ + ld r5,HSTATE_KVM_VCORE(r13) + ld r6,VCORE_TB_OFFSET_APPL(r5) + add r8,r8,r6 + mftb r7 + subf r3,r7,r8 + mtspr SPRN_DEC,r3 + /* Check if HDEC expires soon */ mfspr r3, SPRN_HDEC EXTEND_HDEC(r3) @@ -1597,8 +1598,27 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_TYPE_RADIX) guest_bypass: stw r12, STACK_SLOT_TRAP(r1) - mr r3, r12 + + /* Save DEC */ + /* Do this before kvmhv_commence_exit so we know TB is guest TB */ + ld r3, HSTATE_KVM_VCORE(r13) + mfspr r5,SPRN_DEC + mftb r6 + /* On P9, if the guest has large decr enabled, don't sign extend */ +BEGIN_FTR_SECTION + ld r4, VCORE_LPCR(r3) + andis. r4, r4, LPCR_LD@h + bne 16f +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300) + extsw r5,r5 +16: add r5,r5,r6 + /* r5 is a guest timebase value here, convert to host TB */ + ld r4,VCORE_TB_OFFSET_APPL(r3) + subf r5,r4,r5 + std r5,VCPU_DEC_EXPIRES(r9) + /* Increment exit count, poke other threads to exit */ + mr r3, r12 bl kvmhv_commence_exit nop ld r9, HSTATE_KVM_VCPU(r13) @@ -1639,23 +1659,6 @@ guest_bypass: mtspr SPRN_PURR,r3 mtspr SPRN_SPURR,r4 - /* Save DEC */ - ld r3, HSTATE_KVM_VCORE(r13) - mfspr r5,SPRN_DEC - mftb r6 - /* On P9, if the guest has large decr enabled, don't sign extend */ -BEGIN_FTR_SECTION - ld r4, VCORE_LPCR(r3) - andis. r4, r4, LPCR_LD@h - bne 16f -END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300) - extsw r5,r5 -16: add r5,r5,r6 - /* r5 is a guest timebase value here, convert to host TB */ - ld r4,VCORE_TB_OFFSET(r3) - subf r5,r4,r5 - std r5,VCPU_DEC_EXPIRES(r9) - BEGIN_FTR_SECTION b 8f END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S) @@ -1905,6 +1908,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300) cmpwi cr2, r0, 0 beq cr2, 4f + /* + * Radix: do eieio; tlbsync; ptesync sequence in case we + * interrupted the guest between a tlbie and a ptesync. + */ + eieio + tlbsync + ptesync + /* Radix: Handle the case where the guest used an illegal PID */ LOAD_REG_ADDR(r4, mmu_base_pid) lwz r3, VCPU_GUEST_PID(r9) @@ -2017,9 +2028,11 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S) 27: /* Subtract timebase offset from timebase */ - ld r8,VCORE_TB_OFFSET(r5) + ld r8, VCORE_TB_OFFSET_APPL(r5) cmpdi r8,0 beq 17f + li r0, 0 + std r0, VCORE_TB_OFFSET_APPL(r5) mftb r6 /* current guest timebase */ subf r8,r8,r6 mtspr SPRN_TBU40,r8 /* update upper 40 bits */ @@ -2700,7 +2713,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300) add r3, r3, r5 ld r4, HSTATE_KVM_VCPU(r13) ld r5, HSTATE_KVM_VCORE(r13) - ld r6, VCORE_TB_OFFSET(r5) + ld r6, VCORE_TB_OFFSET_APPL(r5) subf r3, r6, r3 /* convert to host TB value */ std r3, VCPU_DEC_EXPIRES(r4) @@ -2799,7 +2812,7 @@ END_FTR_SECTION(CPU_FTR_TM | CPU_FTR_P9_TM_HV_ASSIST, 0) /* Restore guest decrementer */ ld r3, VCPU_DEC_EXPIRES(r4) ld r5, HSTATE_KVM_VCORE(r13) - ld r6, VCORE_TB_OFFSET(r5) + ld r6, VCORE_TB_OFFSET_APPL(r5) add r3, r3, r6 /* convert host TB to guest TB value */ mftb r7 subf r3, r7, r3 @@ -3606,12 +3619,9 @@ kvmppc_fix_pmao: */ kvmhv_start_timing: ld r5, HSTATE_KVM_VCORE(r13) - lbz r6, VCORE_IN_GUEST(r5) - cmpwi r6, 0 - beq 5f /* if in guest, need to */ - ld r6, VCORE_TB_OFFSET(r5) /* subtract timebase offset */ -5: mftb r5 - subf r5, r6, r5 + ld r6, VCORE_TB_OFFSET_APPL(r5) + mftb r5 + subf r5, r6, r5 /* subtract current timebase offset */ std r3, VCPU_CUR_ACTIVITY(r4) std r5, VCPU_ACTIVITY_START(r4) blr @@ -3622,15 +3632,12 @@ kvmhv_start_timing: */ kvmhv_accumulate_time: ld r5, HSTATE_KVM_VCORE(r13) - lbz r8, VCORE_IN_GUEST(r5) - cmpwi r8, 0 - beq 4f /* if in guest, need to */ - ld r8, VCORE_TB_OFFSET(r5) /* subtract timebase offset */ -4: ld r5, VCPU_CUR_ACTIVITY(r4) + ld r8, VCORE_TB_OFFSET_APPL(r5) + ld r5, VCPU_CUR_ACTIVITY(r4) ld r6, VCPU_ACTIVITY_START(r4) std r3, VCPU_CUR_ACTIVITY(r4) mftb r7 - subf r7, r8, r7 + subf r7, r8, r7 /* subtract current timebase offset */ std r7, VCPU_ACTIVITY_START(r4) cmpdi r5, 0 beqlr diff --git a/arch/powerpc/kvm/book3s_xive_template.c b/arch/powerpc/kvm/book3s_xive_template.c index c7a5deadd1cc78..99c3620b40d95b 100644 --- a/arch/powerpc/kvm/book3s_xive_template.c +++ b/arch/powerpc/kvm/book3s_xive_template.c @@ -11,6 +11,9 @@ #define XGLUE(a,b) a##b #define GLUE(a,b) XGLUE(a,b) +/* Dummy interrupt used when taking interrupts out of a queue in H_CPPR */ +#define XICS_DUMMY 1 + static void GLUE(X_PFX,ack_pending)(struct kvmppc_xive_vcpu *xc) { u8 cppr; @@ -205,6 +208,10 @@ static u32 GLUE(X_PFX,scan_interrupts)(struct kvmppc_xive_vcpu *xc, goto skip_ipi; } + /* If it's the dummy interrupt, continue searching */ + if (hirq == XICS_DUMMY) + goto skip_ipi; + /* If fetching, update queue pointers */ if (scan_type == scan_fetch) { q->idx = idx; @@ -385,9 +392,76 @@ static void GLUE(X_PFX,push_pending_to_hw)(struct kvmppc_xive_vcpu *xc) __x_writeb(prio, __x_tima + TM_SPC_SET_OS_PENDING); } +static void GLUE(X_PFX,scan_for_rerouted_irqs)(struct kvmppc_xive *xive, + struct kvmppc_xive_vcpu *xc) +{ + unsigned int prio; + + /* For each priority that is now masked */ + for (prio = xc->cppr; prio < KVMPPC_XIVE_Q_COUNT; prio++) { + struct xive_q *q = &xc->queues[prio]; + struct kvmppc_xive_irq_state *state; + struct kvmppc_xive_src_block *sb; + u32 idx, toggle, entry, irq, hw_num; + struct xive_irq_data *xd; + __be32 *qpage; + u16 src; + + idx = q->idx; + toggle = q->toggle; + qpage = READ_ONCE(q->qpage); + if (!qpage) + continue; + + /* For each interrupt in the queue */ + for (;;) { + entry = be32_to_cpup(qpage + idx); + + /* No more ? */ + if ((entry >> 31) == toggle) + break; + irq = entry & 0x7fffffff; + + /* Skip dummies and IPIs */ + if (irq == XICS_DUMMY || irq == XICS_IPI) + goto next; + sb = kvmppc_xive_find_source(xive, irq, &src); + if (!sb) + goto next; + state = &sb->irq_state[src]; + + /* Has it been rerouted ? */ + if (xc->server_num == state->act_server) + goto next; + + /* + * Allright, it *has* been re-routed, kill it from + * the queue. + */ + qpage[idx] = cpu_to_be32((entry & 0x80000000) | XICS_DUMMY); + + /* Find the HW interrupt */ + kvmppc_xive_select_irq(state, &hw_num, &xd); + + /* If it's not an LSI, set PQ to 11 the EOI will force a resend */ + if (!(xd->flags & XIVE_IRQ_FLAG_LSI)) + GLUE(X_PFX,esb_load)(xd, XIVE_ESB_SET_PQ_11); + + /* EOI the source */ + GLUE(X_PFX,source_eoi)(hw_num, xd); + + next: + idx = (idx + 1) & q->msk; + if (idx == 0) + toggle ^= 1; + } + } +} + X_STATIC int GLUE(X_PFX,h_cppr)(struct kvm_vcpu *vcpu, unsigned long cppr) { struct kvmppc_xive_vcpu *xc = vcpu->arch.xive_vcpu; + struct kvmppc_xive *xive = vcpu->kvm->arch.xive; u8 old_cppr; pr_devel("H_CPPR(cppr=%ld)\n", cppr); @@ -407,14 +481,34 @@ X_STATIC int GLUE(X_PFX,h_cppr)(struct kvm_vcpu *vcpu, unsigned long cppr) */ smp_mb(); - /* - * We are masking less, we need to look for pending things - * to deliver and set VP pending bits accordingly to trigger - * a new interrupt otherwise we might miss MFRR changes for - * which we have optimized out sending an IPI signal. - */ - if (cppr > old_cppr) + if (cppr > old_cppr) { + /* + * We are masking less, we need to look for pending things + * to deliver and set VP pending bits accordingly to trigger + * a new interrupt otherwise we might miss MFRR changes for + * which we have optimized out sending an IPI signal. + */ GLUE(X_PFX,push_pending_to_hw)(xc); + } else { + /* + * We are masking more, we need to check the queue for any + * interrupt that has been routed to another CPU, take + * it out (replace it with the dummy) and retrigger it. + * + * This is necessary since those interrupts may otherwise + * never be processed, at least not until this CPU restores + * its CPPR. + * + * This is in theory racy vs. HW adding new interrupts to + * the queue. In practice this works because the interesting + * cases are when the guest has done a set_xive() to move the + * interrupt away, which flushes the xive, followed by the + * target CPU doing a H_CPPR. So any new interrupt coming into + * the queue must still be routed to us and isn't a source + * of concern. + */ + GLUE(X_PFX,scan_for_rerouted_irqs)(xive, xc); + } /* Apply new CPPR */ xc->hw_cppr = cppr; diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c index 288fe4f0db4ea3..e1bcdc32a851cf 100644 --- a/arch/powerpc/lib/feature-fixups.c +++ b/arch/powerpc/lib/feature-fixups.c @@ -23,6 +23,7 @@ #include #include #include +#include #include struct fixup_entry { @@ -117,6 +118,120 @@ void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end) } #ifdef CONFIG_PPC_BOOK3S_64 +void do_stf_entry_barrier_fixups(enum stf_barrier_type types) +{ + unsigned int instrs[3], *dest; + long *start, *end; + int i; + + start = PTRRELOC(&__start___stf_entry_barrier_fixup), + end = PTRRELOC(&__stop___stf_entry_barrier_fixup); + + instrs[0] = 0x60000000; /* nop */ + instrs[1] = 0x60000000; /* nop */ + instrs[2] = 0x60000000; /* nop */ + + i = 0; + if (types & STF_BARRIER_FALLBACK) { + instrs[i++] = 0x7d4802a6; /* mflr r10 */ + instrs[i++] = 0x60000000; /* branch patched below */ + instrs[i++] = 0x7d4803a6; /* mtlr r10 */ + } else if (types & STF_BARRIER_EIEIO) { + instrs[i++] = 0x7e0006ac; /* eieio + bit 6 hint */ + } else if (types & STF_BARRIER_SYNC_ORI) { + instrs[i++] = 0x7c0004ac; /* hwsync */ + instrs[i++] = 0xe94d0000; /* ld r10,0(r13) */ + instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */ + } + + for (i = 0; start < end; start++, i++) { + dest = (void *)start + *start; + + pr_devel("patching dest %lx\n", (unsigned long)dest); + + patch_instruction(dest, instrs[0]); + + if (types & STF_BARRIER_FALLBACK) + patch_branch(dest + 1, (unsigned long)&stf_barrier_fallback, + BRANCH_SET_LINK); + else + patch_instruction(dest + 1, instrs[1]); + + patch_instruction(dest + 2, instrs[2]); + } + + printk(KERN_DEBUG "stf-barrier: patched %d entry locations (%s barrier)\n", i, + (types == STF_BARRIER_NONE) ? "no" : + (types == STF_BARRIER_FALLBACK) ? "fallback" : + (types == STF_BARRIER_EIEIO) ? "eieio" : + (types == (STF_BARRIER_SYNC_ORI)) ? "hwsync" + : "unknown"); +} + +void do_stf_exit_barrier_fixups(enum stf_barrier_type types) +{ + unsigned int instrs[6], *dest; + long *start, *end; + int i; + + start = PTRRELOC(&__start___stf_exit_barrier_fixup), + end = PTRRELOC(&__stop___stf_exit_barrier_fixup); + + instrs[0] = 0x60000000; /* nop */ + instrs[1] = 0x60000000; /* nop */ + instrs[2] = 0x60000000; /* nop */ + instrs[3] = 0x60000000; /* nop */ + instrs[4] = 0x60000000; /* nop */ + instrs[5] = 0x60000000; /* nop */ + + i = 0; + if (types & STF_BARRIER_FALLBACK || types & STF_BARRIER_SYNC_ORI) { + if (cpu_has_feature(CPU_FTR_HVMODE)) { + instrs[i++] = 0x7db14ba6; /* mtspr 0x131, r13 (HSPRG1) */ + instrs[i++] = 0x7db04aa6; /* mfspr r13, 0x130 (HSPRG0) */ + } else { + instrs[i++] = 0x7db243a6; /* mtsprg 2,r13 */ + instrs[i++] = 0x7db142a6; /* mfsprg r13,1 */ + } + instrs[i++] = 0x7c0004ac; /* hwsync */ + instrs[i++] = 0xe9ad0000; /* ld r13,0(r13) */ + instrs[i++] = 0x63ff0000; /* ori 31,31,0 speculation barrier */ + if (cpu_has_feature(CPU_FTR_HVMODE)) { + instrs[i++] = 0x7db14aa6; /* mfspr r13, 0x131 (HSPRG1) */ + } else { + instrs[i++] = 0x7db242a6; /* mfsprg r13,2 */ + } + } else if (types & STF_BARRIER_EIEIO) { + instrs[i++] = 0x7e0006ac; /* eieio + bit 6 hint */ + } + + for (i = 0; start < end; start++, i++) { + dest = (void *)start + *start; + + pr_devel("patching dest %lx\n", (unsigned long)dest); + + patch_instruction(dest, instrs[0]); + patch_instruction(dest + 1, instrs[1]); + patch_instruction(dest + 2, instrs[2]); + patch_instruction(dest + 3, instrs[3]); + patch_instruction(dest + 4, instrs[4]); + patch_instruction(dest + 5, instrs[5]); + } + printk(KERN_DEBUG "stf-barrier: patched %d exit locations (%s barrier)\n", i, + (types == STF_BARRIER_NONE) ? "no" : + (types == STF_BARRIER_FALLBACK) ? "fallback" : + (types == STF_BARRIER_EIEIO) ? "eieio" : + (types == (STF_BARRIER_SYNC_ORI)) ? "hwsync" + : "unknown"); +} + + +void do_stf_barrier_fixups(enum stf_barrier_type types) +{ + do_stf_entry_barrier_fixups(types); + do_stf_exit_barrier_fixups(types); +} + void do_rfi_flush_fixups(enum l1d_flush_type types) { unsigned int instrs[3], *dest; diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c index 1bceb95f422d0f..5584247f502929 100644 --- a/arch/powerpc/platforms/powernv/opal-nvram.c +++ b/arch/powerpc/platforms/powernv/opal-nvram.c @@ -44,6 +44,10 @@ static ssize_t opal_nvram_read(char *buf, size_t count, loff_t *index) return count; } +/* + * This can be called in the panic path with interrupts off, so use + * mdelay in that case. + */ static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index) { s64 rc = OPAL_BUSY; @@ -58,10 +62,16 @@ static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index) while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { rc = opal_write_nvram(__pa(buf), count, off); if (rc == OPAL_BUSY_EVENT) { - msleep(OPAL_BUSY_DELAY_MS); + if (in_interrupt() || irqs_disabled()) + mdelay(OPAL_BUSY_DELAY_MS); + else + msleep(OPAL_BUSY_DELAY_MS); opal_poll_events(NULL); } else if (rc == OPAL_BUSY) { - msleep(OPAL_BUSY_DELAY_MS); + if (in_interrupt() || irqs_disabled()) + mdelay(OPAL_BUSY_DELAY_MS); + else + msleep(OPAL_BUSY_DELAY_MS); } } diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index ef8c9ce53a6169..a6648ec99ca76c 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -131,6 +131,7 @@ static void __init pnv_setup_arch(void) set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT); pnv_setup_rfi_flush(); + setup_stf_barrier(); /* Initialize SMP */ pnv_smp_init(); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index b55ad4286dc7f8..fdb32e056ef42a 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -710,6 +710,7 @@ static void __init pSeries_setup_arch(void) fwnmi_init(); pseries_setup_rfi_flush(); + setup_stf_barrier(); /* By default, only probe PCI (can be overridden by rtas_pci) */ pci_add_flags(PCI_PROBE_ONLY); diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index 6176fe9795caf9..941d8cc6c9f599 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -261,9 +261,9 @@ CONFIG_IP_VS_NQ=m CONFIG_IP_VS_FTP=m CONFIG_IP_VS_PE_SIP=m CONFIG_NF_CONNTRACK_IPV4=m -CONFIG_NF_TABLES_IPV4=m +CONFIG_NF_TABLES_IPV4=y CONFIG_NFT_CHAIN_ROUTE_IPV4=m -CONFIG_NF_TABLES_ARP=m +CONFIG_NF_TABLES_ARP=y CONFIG_NFT_CHAIN_NAT_IPV4=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_AH=m @@ -284,7 +284,7 @@ CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m CONFIG_NF_CONNTRACK_IPV6=m -CONFIG_NF_TABLES_IPV6=m +CONFIG_NF_TABLES_IPV6=y CONFIG_NFT_CHAIN_ROUTE_IPV6=m CONFIG_NFT_CHAIN_NAT_IPV6=m CONFIG_IP6_NF_IPTABLES=m @@ -305,7 +305,7 @@ CONFIG_IP6_NF_RAW=m CONFIG_IP6_NF_SECURITY=m CONFIG_IP6_NF_NAT=m CONFIG_IP6_NF_TARGET_MASQUERADE=m -CONFIG_NF_TABLES_BRIDGE=m +CONFIG_NF_TABLES_BRIDGE=y CONFIG_RDS=m CONFIG_RDS_RDMA=m CONFIG_RDS_TCP=m @@ -604,7 +604,6 @@ CONFIG_DETECT_HUNG_TASK=y CONFIG_WQ_WATCHDOG=y CONFIG_PANIC_ON_OOPS=y CONFIG_DEBUG_TIMEKEEPING=y -CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y CONFIG_PROVE_LOCKING=y CONFIG_LOCK_STAT=y CONFIG_DEBUG_LOCKDEP=y diff --git a/arch/s390/configs/performance_defconfig b/arch/s390/configs/performance_defconfig index c105bcc6d7a6fc..eb6f75f242089b 100644 --- a/arch/s390/configs/performance_defconfig +++ b/arch/s390/configs/performance_defconfig @@ -259,9 +259,9 @@ CONFIG_IP_VS_NQ=m CONFIG_IP_VS_FTP=m CONFIG_IP_VS_PE_SIP=m CONFIG_NF_CONNTRACK_IPV4=m -CONFIG_NF_TABLES_IPV4=m +CONFIG_NF_TABLES_IPV4=y CONFIG_NFT_CHAIN_ROUTE_IPV4=m -CONFIG_NF_TABLES_ARP=m +CONFIG_NF_TABLES_ARP=y CONFIG_NFT_CHAIN_NAT_IPV4=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_AH=m @@ -282,7 +282,7 @@ CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m CONFIG_NF_CONNTRACK_IPV6=m -CONFIG_NF_TABLES_IPV6=m +CONFIG_NF_TABLES_IPV6=y CONFIG_NFT_CHAIN_ROUTE_IPV6=m CONFIG_NFT_CHAIN_NAT_IPV6=m CONFIG_IP6_NF_IPTABLES=m @@ -303,7 +303,7 @@ CONFIG_IP6_NF_RAW=m CONFIG_IP6_NF_SECURITY=m CONFIG_IP6_NF_NAT=m CONFIG_IP6_NF_TARGET_MASQUERADE=m -CONFIG_NF_TABLES_BRIDGE=m +CONFIG_NF_TABLES_BRIDGE=y CONFIG_RDS=m CONFIG_RDS_RDMA=m CONFIG_RDS_TCP=m diff --git a/arch/s390/crypto/crc32be-vx.S b/arch/s390/crypto/crc32be-vx.S index e8077f0971f89f..2bf01ba44107cd 100644 --- a/arch/s390/crypto/crc32be-vx.S +++ b/arch/s390/crypto/crc32be-vx.S @@ -13,6 +13,7 @@ */ #include +#include #include /* Vector register range containing CRC-32 constants */ @@ -67,6 +68,8 @@ .previous + GEN_BR_THUNK %r14 + .text /* * The CRC-32 function(s) use these calling conventions: @@ -203,6 +206,6 @@ ENTRY(crc32_be_vgfm_16) .Ldone: VLGVF %r2,%v2,3 - br %r14 + BR_EX %r14 .previous diff --git a/arch/s390/crypto/crc32le-vx.S b/arch/s390/crypto/crc32le-vx.S index d8c67a58c0c53b..7d6f568bd3ad1f 100644 --- a/arch/s390/crypto/crc32le-vx.S +++ b/arch/s390/crypto/crc32le-vx.S @@ -14,6 +14,7 @@ */ #include +#include #include /* Vector register range containing CRC-32 constants */ @@ -76,6 +77,7 @@ .previous + GEN_BR_THUNK %r14 .text @@ -264,6 +266,6 @@ crc32_le_vgfm_generic: .Ldone: VLGVF %r2,%v2,2 - br %r14 + BR_EX %r14 .previous diff --git a/arch/s390/include/asm/nospec-insn.h b/arch/s390/include/asm/nospec-insn.h new file mode 100644 index 00000000000000..a01f81186e86ac --- /dev/null +++ b/arch/s390/include/asm/nospec-insn.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_S390_NOSPEC_ASM_H +#define _ASM_S390_NOSPEC_ASM_H + +#include +#include +#include + +#ifdef __ASSEMBLY__ + +#ifdef CONFIG_EXPOLINE + +_LC_BR_R1 = __LC_BR_R1 + +/* + * The expoline macros are used to create thunks in the same format + * as gcc generates them. The 'comdat' section flag makes sure that + * the various thunks are merged into a single copy. + */ + .macro __THUNK_PROLOG_NAME name + .pushsection .text.\name,"axG",@progbits,\name,comdat + .globl \name + .hidden \name + .type \name,@function +\name: + CFI_STARTPROC + .endm + + .macro __THUNK_EPILOG + CFI_ENDPROC + .popsection + .endm + + .macro __THUNK_PROLOG_BR r1,r2 + __THUNK_PROLOG_NAME __s390x_indirect_jump_r\r2\()use_r\r1 + .endm + + .macro __THUNK_PROLOG_BC d0,r1,r2 + __THUNK_PROLOG_NAME __s390x_indirect_branch_\d0\()_\r2\()use_\r1 + .endm + + .macro __THUNK_BR r1,r2 + jg __s390x_indirect_jump_r\r2\()use_r\r1 + .endm + + .macro __THUNK_BC d0,r1,r2 + jg __s390x_indirect_branch_\d0\()_\r2\()use_\r1 + .endm + + .macro __THUNK_BRASL r1,r2,r3 + brasl \r1,__s390x_indirect_jump_r\r3\()use_r\r2 + .endm + + .macro __DECODE_RR expand,reg,ruse + .set __decode_fail,1 + .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + .ifc \reg,%r\r1 + .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + .ifc \ruse,%r\r2 + \expand \r1,\r2 + .set __decode_fail,0 + .endif + .endr + .endif + .endr + .if __decode_fail == 1 + .error "__DECODE_RR failed" + .endif + .endm + + .macro __DECODE_RRR expand,rsave,rtarget,ruse + .set __decode_fail,1 + .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + .ifc \rsave,%r\r1 + .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + .ifc \rtarget,%r\r2 + .irp r3,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + .ifc \ruse,%r\r3 + \expand \r1,\r2,\r3 + .set __decode_fail,0 + .endif + .endr + .endif + .endr + .endif + .endr + .if __decode_fail == 1 + .error "__DECODE_RRR failed" + .endif + .endm + + .macro __DECODE_DRR expand,disp,reg,ruse + .set __decode_fail,1 + .irp r1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + .ifc \reg,%r\r1 + .irp r2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + .ifc \ruse,%r\r2 + \expand \disp,\r1,\r2 + .set __decode_fail,0 + .endif + .endr + .endif + .endr + .if __decode_fail == 1 + .error "__DECODE_DRR failed" + .endif + .endm + + .macro __THUNK_EX_BR reg,ruse + # Be very careful when adding instructions to this macro! + # The ALTERNATIVE replacement code has a .+10 which targets + # the "br \reg" after the code has been patched. +#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES + exrl 0,555f + j . +#else + .ifc \reg,%r1 + ALTERNATIVE "ex %r0,_LC_BR_R1", ".insn ril,0xc60000000000,0,.+10", 35 + j . + .else + larl \ruse,555f + ex 0,0(\ruse) + j . + .endif +#endif +555: br \reg + .endm + + .macro __THUNK_EX_BC disp,reg,ruse +#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES + exrl 0,556f + j . +#else + larl \ruse,556f + ex 0,0(\ruse) + j . +#endif +556: b \disp(\reg) + .endm + + .macro GEN_BR_THUNK reg,ruse=%r1 + __DECODE_RR __THUNK_PROLOG_BR,\reg,\ruse + __THUNK_EX_BR \reg,\ruse + __THUNK_EPILOG + .endm + + .macro GEN_B_THUNK disp,reg,ruse=%r1 + __DECODE_DRR __THUNK_PROLOG_BC,\disp,\reg,\ruse + __THUNK_EX_BC \disp,\reg,\ruse + __THUNK_EPILOG + .endm + + .macro BR_EX reg,ruse=%r1 +557: __DECODE_RR __THUNK_BR,\reg,\ruse + .pushsection .s390_indirect_branches,"a",@progbits + .long 557b-. + .popsection + .endm + + .macro B_EX disp,reg,ruse=%r1 +558: __DECODE_DRR __THUNK_BC,\disp,\reg,\ruse + .pushsection .s390_indirect_branches,"a",@progbits + .long 558b-. + .popsection + .endm + + .macro BASR_EX rsave,rtarget,ruse=%r1 +559: __DECODE_RRR __THUNK_BRASL,\rsave,\rtarget,\ruse + .pushsection .s390_indirect_branches,"a",@progbits + .long 559b-. + .popsection + .endm + +#else + .macro GEN_BR_THUNK reg,ruse=%r1 + .endm + + .macro GEN_B_THUNK disp,reg,ruse=%r1 + .endm + + .macro BR_EX reg,ruse=%r1 + br \reg + .endm + + .macro B_EX disp,reg,ruse=%r1 + b \disp(\reg) + .endm + + .macro BASR_EX rsave,rtarget,ruse=%r1 + basr \rsave,\rtarget + .endm +#endif + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_S390_NOSPEC_ASM_H */ diff --git a/arch/s390/include/asm/purgatory.h b/arch/s390/include/asm/purgatory.h index e297bcfc476f65..6090670df51fce 100644 --- a/arch/s390/include/asm/purgatory.h +++ b/arch/s390/include/asm/purgatory.h @@ -13,5 +13,11 @@ int verify_sha256_digest(void); +extern u64 kernel_entry; +extern u64 kernel_type; + +extern u64 crash_start; +extern u64 crash_size; + #endif /* __ASSEMBLY__ */ #endif /* _S390_PURGATORY_H_ */ diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 84ea6225efb4be..f92dd8ed3884ae 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -65,6 +65,7 @@ obj-y += nospec-branch.o extra-y += head.o head64.o vmlinux.lds +obj-$(CONFIG_SYSFS) += nospec-sysfs.o CFLAGS_REMOVE_nospec-branch.o += $(CC_FLAGS_EXPOLINE) obj-$(CONFIG_MODULES) += module.o diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index eb2a5c0443cd9c..11aea745a2a6eb 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -181,6 +181,7 @@ int main(void) OFFSET(__LC_MACHINE_FLAGS, lowcore, machine_flags); OFFSET(__LC_PREEMPT_COUNT, lowcore, preempt_count); OFFSET(__LC_GMAP, lowcore, gmap); + OFFSET(__LC_BR_R1, lowcore, br_r1_trampoline); /* software defined ABI-relevant lowcore locations 0xe00 - 0xe20 */ OFFSET(__LC_DUMP_REIPL, lowcore, ipib); /* hardware defined lowcore locations 0x1000 - 0x18ff */ diff --git a/arch/s390/kernel/base.S b/arch/s390/kernel/base.S index f6c56009e82247..b65874b0b412e4 100644 --- a/arch/s390/kernel/base.S +++ b/arch/s390/kernel/base.S @@ -9,18 +9,22 @@ #include #include +#include #include #include + GEN_BR_THUNK %r9 + GEN_BR_THUNK %r14 + ENTRY(s390_base_mcck_handler) basr %r13,0 0: lg %r15,__LC_PANIC_STACK # load panic stack aghi %r15,-STACK_FRAME_OVERHEAD larl %r1,s390_base_mcck_handler_fn - lg %r1,0(%r1) - ltgr %r1,%r1 + lg %r9,0(%r1) + ltgr %r9,%r9 jz 1f - basr %r14,%r1 + BASR_EX %r14,%r9 1: la %r1,4095 lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1) lpswe __LC_MCK_OLD_PSW @@ -37,10 +41,10 @@ ENTRY(s390_base_ext_handler) basr %r13,0 0: aghi %r15,-STACK_FRAME_OVERHEAD larl %r1,s390_base_ext_handler_fn - lg %r1,0(%r1) - ltgr %r1,%r1 + lg %r9,0(%r1) + ltgr %r9,%r9 jz 1f - basr %r14,%r1 + BASR_EX %r14,%r9 1: lmg %r0,%r15,__LC_SAVE_AREA_ASYNC ni __LC_EXT_OLD_PSW+1,0xfd # clear wait state bit lpswe __LC_EXT_OLD_PSW @@ -57,10 +61,10 @@ ENTRY(s390_base_pgm_handler) basr %r13,0 0: aghi %r15,-STACK_FRAME_OVERHEAD larl %r1,s390_base_pgm_handler_fn - lg %r1,0(%r1) - ltgr %r1,%r1 + lg %r9,0(%r1) + ltgr %r9,%r9 jz 1f - basr %r14,%r1 + BASR_EX %r14,%r9 lmg %r0,%r15,__LC_SAVE_AREA_SYNC lpswe __LC_PGM_OLD_PSW 1: lpswe disabled_wait_psw-0b(%r13) @@ -117,7 +121,7 @@ ENTRY(diag308_reset) larl %r4,.Lcontinue_psw # Restore PSW flags lpswe 0(%r4) .Lcontinue: - br %r14 + BR_EX %r14 .align 16 .Lrestart_psw: .long 0x00080000,0x80000000 + .Lrestart_part2 diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 3f22f139a0413f..f03402efab4b41 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -28,6 +28,7 @@ #include #include #include +#include __PT_R0 = __PT_GPRS __PT_R1 = __PT_GPRS + 8 @@ -183,67 +184,9 @@ _LPP_OFFSET = __LC_LPP "jnz .+8; .long 0xb2e8d000", 82 .endm -#ifdef CONFIG_EXPOLINE - - .macro GEN_BR_THUNK name,reg,tmp - .section .text.\name,"axG",@progbits,\name,comdat - .globl \name - .hidden \name - .type \name,@function -\name: - CFI_STARTPROC -#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES - exrl 0,0f -#else - larl \tmp,0f - ex 0,0(\tmp) -#endif - j . -0: br \reg - CFI_ENDPROC - .endm - - GEN_BR_THUNK __s390x_indirect_jump_r1use_r9,%r9,%r1 - GEN_BR_THUNK __s390x_indirect_jump_r1use_r14,%r14,%r1 - GEN_BR_THUNK __s390x_indirect_jump_r11use_r14,%r14,%r11 - - .macro BASR_R14_R9 -0: brasl %r14,__s390x_indirect_jump_r1use_r9 - .pushsection .s390_indirect_branches,"a",@progbits - .long 0b-. - .popsection - .endm - - .macro BR_R1USE_R14 -0: jg __s390x_indirect_jump_r1use_r14 - .pushsection .s390_indirect_branches,"a",@progbits - .long 0b-. - .popsection - .endm - - .macro BR_R11USE_R14 -0: jg __s390x_indirect_jump_r11use_r14 - .pushsection .s390_indirect_branches,"a",@progbits - .long 0b-. - .popsection - .endm - -#else /* CONFIG_EXPOLINE */ - - .macro BASR_R14_R9 - basr %r14,%r9 - .endm - - .macro BR_R1USE_R14 - br %r14 - .endm - - .macro BR_R11USE_R14 - br %r14 - .endm - -#endif /* CONFIG_EXPOLINE */ - + GEN_BR_THUNK %r9 + GEN_BR_THUNK %r14 + GEN_BR_THUNK %r14,%r11 .section .kprobes.text, "ax" .Ldummy: @@ -260,7 +203,7 @@ _LPP_OFFSET = __LC_LPP ENTRY(__bpon) .globl __bpon BPON - BR_R1USE_R14 + BR_EX %r14 /* * Scheduler resume function, called by switch_to @@ -284,7 +227,7 @@ ENTRY(__switch_to) mvc __LC_CURRENT_PID(4,%r0),0(%r3) # store pid of next lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task ALTERNATIVE "", ".insn s,0xb2800000,_LPP_OFFSET", 40 - BR_R1USE_R14 + BR_EX %r14 .L__critical_start: @@ -351,7 +294,7 @@ sie_exit: xgr %r5,%r5 lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers lg %r2,__SF_SIE_REASON(%r15) # return exit reason code - BR_R1USE_R14 + BR_EX %r14 .Lsie_fault: lghi %r14,-EFAULT stg %r14,__SF_SIE_REASON(%r15) # set exit reason code @@ -410,7 +353,7 @@ ENTRY(system_call) lgf %r9,0(%r8,%r10) # get system call add. TSTMSK __TI_flags(%r12),_TIF_TRACE jnz .Lsysc_tracesys - BASR_R14_R9 # call sys_xxxx + BASR_EX %r14,%r9 # call sys_xxxx stg %r2,__PT_R2(%r11) # store return value .Lsysc_return: @@ -595,7 +538,7 @@ ENTRY(system_call) lmg %r3,%r7,__PT_R3(%r11) stg %r7,STACK_FRAME_OVERHEAD(%r15) lg %r2,__PT_ORIG_GPR2(%r11) - BASR_R14_R9 # call sys_xxx + BASR_EX %r14,%r9 # call sys_xxx stg %r2,__PT_R2(%r11) # store return value .Lsysc_tracenogo: TSTMSK __TI_flags(%r12),_TIF_TRACE @@ -619,7 +562,7 @@ ENTRY(ret_from_fork) lmg %r9,%r10,__PT_R9(%r11) # load gprs ENTRY(kernel_thread_starter) la %r2,0(%r10) - BASR_R14_R9 + BASR_EX %r14,%r9 j .Lsysc_tracenogo /* @@ -701,7 +644,7 @@ ENTRY(pgm_check_handler) je .Lpgm_return lgf %r9,0(%r10,%r1) # load address of handler routine lgr %r2,%r11 # pass pointer to pt_regs - BASR_R14_R9 # branch to interrupt-handler + BASR_EX %r14,%r9 # branch to interrupt-handler .Lpgm_return: LOCKDEP_SYS_EXIT tm __PT_PSW+1(%r11),0x01 # returning to user ? @@ -1019,7 +962,7 @@ ENTRY(psw_idle) stpt __TIMER_IDLE_ENTER(%r2) .Lpsw_idle_lpsw: lpswe __SF_EMPTY(%r15) - BR_R1USE_R14 + BR_EX %r14 .Lpsw_idle_end: /* @@ -1061,7 +1004,7 @@ ENTRY(save_fpu_regs) .Lsave_fpu_regs_done: oi __LC_CPU_FLAGS+7,_CIF_FPU .Lsave_fpu_regs_exit: - BR_R1USE_R14 + BR_EX %r14 .Lsave_fpu_regs_end: EXPORT_SYMBOL(save_fpu_regs) @@ -1107,7 +1050,7 @@ load_fpu_regs: .Lload_fpu_regs_done: ni __LC_CPU_FLAGS+7,255-_CIF_FPU .Lload_fpu_regs_exit: - BR_R1USE_R14 + BR_EX %r14 .Lload_fpu_regs_end: .L__critical_end: @@ -1322,7 +1265,7 @@ cleanup_critical: jl 0f clg %r9,BASED(.Lcleanup_table+104) # .Lload_fpu_regs_end jl .Lcleanup_load_fpu_regs -0: BR_R11USE_R14 +0: BR_EX %r14 .align 8 .Lcleanup_table: @@ -1358,7 +1301,7 @@ cleanup_critical: ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE lctlg %c1,%c1,__LC_USER_ASCE # load primary asce larl %r9,sie_exit # skip forward to sie_exit - BR_R11USE_R14 + BR_EX %r14 #endif .Lcleanup_system_call: @@ -1412,7 +1355,7 @@ cleanup_critical: stg %r15,56(%r11) # r15 stack pointer # set new psw address and exit larl %r9,.Lsysc_do_svc - BR_R11USE_R14 + BR_EX %r14,%r11 .Lcleanup_system_call_insn: .quad system_call .quad .Lsysc_stmg @@ -1424,7 +1367,7 @@ cleanup_critical: .Lcleanup_sysc_tif: larl %r9,.Lsysc_tif - BR_R11USE_R14 + BR_EX %r14,%r11 .Lcleanup_sysc_restore: # check if stpt has been executed @@ -1441,14 +1384,14 @@ cleanup_critical: mvc 0(64,%r11),__PT_R8(%r9) lmg %r0,%r7,__PT_R0(%r9) 1: lmg %r8,%r9,__LC_RETURN_PSW - BR_R11USE_R14 + BR_EX %r14,%r11 .Lcleanup_sysc_restore_insn: .quad .Lsysc_exit_timer .quad .Lsysc_done - 4 .Lcleanup_io_tif: larl %r9,.Lio_tif - BR_R11USE_R14 + BR_EX %r14,%r11 .Lcleanup_io_restore: # check if stpt has been executed @@ -1462,7 +1405,7 @@ cleanup_critical: mvc 0(64,%r11),__PT_R8(%r9) lmg %r0,%r7,__PT_R0(%r9) 1: lmg %r8,%r9,__LC_RETURN_PSW - BR_R11USE_R14 + BR_EX %r14,%r11 .Lcleanup_io_restore_insn: .quad .Lio_exit_timer .quad .Lio_done - 4 @@ -1515,17 +1458,17 @@ cleanup_critical: # prepare return psw nihh %r8,0xfcfd # clear irq & wait state bits lg %r9,48(%r11) # return from psw_idle - BR_R11USE_R14 + BR_EX %r14,%r11 .Lcleanup_idle_insn: .quad .Lpsw_idle_lpsw .Lcleanup_save_fpu_regs: larl %r9,save_fpu_regs - BR_R11USE_R14 + BR_EX %r14,%r11 .Lcleanup_load_fpu_regs: larl %r9,load_fpu_regs - BR_R11USE_R14 + BR_EX %r14,%r11 /* * Integer constants diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 94f2099bceb04c..3d17c41074ca55 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -176,10 +176,9 @@ void do_softirq_own_stack(void) new -= STACK_FRAME_OVERHEAD; ((struct stack_frame *) new)->back_chain = old; asm volatile(" la 15,0(%0)\n" - " basr 14,%2\n" + " brasl 14,__do_softirq\n" " la 15,0(%1)\n" - : : "a" (new), "a" (old), - "a" (__do_softirq) + : : "a" (new), "a" (old) : "0", "1", "2", "3", "4", "5", "14", "cc", "memory" ); } else { diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S index 82df7d80fab220..27110f3294edcd 100644 --- a/arch/s390/kernel/mcount.S +++ b/arch/s390/kernel/mcount.S @@ -9,13 +9,17 @@ #include #include #include +#include #include #include + GEN_BR_THUNK %r1 + GEN_BR_THUNK %r14 + .section .kprobes.text, "ax" ENTRY(ftrace_stub) - br %r14 + BR_EX %r14 #define STACK_FRAME_SIZE (STACK_FRAME_OVERHEAD + __PT_SIZE) #define STACK_PTREGS (STACK_FRAME_OVERHEAD) @@ -23,7 +27,7 @@ ENTRY(ftrace_stub) #define STACK_PTREGS_PSW (STACK_PTREGS + __PT_PSW) ENTRY(_mcount) - br %r14 + BR_EX %r14 EXPORT_SYMBOL(_mcount) @@ -53,7 +57,7 @@ ENTRY(ftrace_caller) #endif lgr %r3,%r14 la %r5,STACK_PTREGS(%r15) - basr %r14,%r1 + BASR_EX %r14,%r1 #ifdef CONFIG_FUNCTION_GRAPH_TRACER # The j instruction gets runtime patched to a nop instruction. # See ftrace_enable_ftrace_graph_caller. @@ -68,7 +72,7 @@ ftrace_graph_caller_end: #endif lg %r1,(STACK_PTREGS_PSW+8)(%r15) lmg %r2,%r15,(STACK_PTREGS_GPRS+2*8)(%r15) - br %r1 + BR_EX %r1 #ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -81,6 +85,6 @@ ENTRY(return_to_handler) aghi %r15,STACK_FRAME_OVERHEAD lgr %r14,%r2 lmg %r2,%r5,32(%r15) - br %r14 + BR_EX %r14 #endif diff --git a/arch/s390/kernel/nospec-branch.c b/arch/s390/kernel/nospec-branch.c index 46d49a11663f59..8ad6a7128b3a5e 100644 --- a/arch/s390/kernel/nospec-branch.c +++ b/arch/s390/kernel/nospec-branch.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include #include -#include #include static int __init nobp_setup_early(char *str) @@ -44,24 +43,6 @@ static int __init nospec_report(void) } arch_initcall(nospec_report); -#ifdef CONFIG_SYSFS -ssize_t cpu_show_spectre_v1(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "Mitigation: __user pointer sanitization\n"); -} - -ssize_t cpu_show_spectre_v2(struct device *dev, - struct device_attribute *attr, char *buf) -{ - if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) - return sprintf(buf, "Mitigation: execute trampolines\n"); - if (__test_facility(82, S390_lowcore.alt_stfle_fac_list)) - return sprintf(buf, "Mitigation: limited branch prediction.\n"); - return sprintf(buf, "Vulnerable\n"); -} -#endif - #ifdef CONFIG_EXPOLINE int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF); @@ -112,7 +93,6 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end) s32 *epo; /* Second part of the instruction replace is always a nop */ - memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x00, 0x00 }, 4); for (epo = start; epo < end; epo++) { instr = (u8 *) epo + *epo; if (instr[0] == 0xc0 && (instr[1] & 0x0f) == 0x04) @@ -133,18 +113,34 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end) br = thunk + (*(int *)(thunk + 2)) * 2; else continue; - if (br[0] != 0x07 || (br[1] & 0xf0) != 0xf0) + /* Check for unconditional branch 0x07f? or 0x47f???? */ + if ((br[0] & 0xbf) != 0x07 || (br[1] & 0xf0) != 0xf0) continue; + + memcpy(insnbuf + 2, (char[]) { 0x47, 0x00, 0x07, 0x00 }, 4); switch (type) { case BRCL_EXPOLINE: - /* brcl to thunk, replace with br + nop */ insnbuf[0] = br[0]; insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f); + if (br[0] == 0x47) { + /* brcl to b, replace with bc + nopr */ + insnbuf[2] = br[2]; + insnbuf[3] = br[3]; + } else { + /* brcl to br, replace with bcr + nop */ + } break; case BRASL_EXPOLINE: - /* brasl to thunk, replace with basr + nop */ - insnbuf[0] = 0x0d; insnbuf[1] = (instr[1] & 0xf0) | (br[1] & 0x0f); + if (br[0] == 0x47) { + /* brasl to b, replace with bas + nopr */ + insnbuf[0] = 0x4d; + insnbuf[2] = br[2]; + insnbuf[3] = br[3]; + } else { + /* brasl to br, replace with basr + nop */ + insnbuf[0] = 0x0d; + } break; } diff --git a/arch/s390/kernel/nospec-sysfs.c b/arch/s390/kernel/nospec-sysfs.c new file mode 100644 index 00000000000000..8affad5f18cb5d --- /dev/null +++ b/arch/s390/kernel/nospec-sysfs.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +ssize_t cpu_show_spectre_v1(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "Mitigation: __user pointer sanitization\n"); +} + +ssize_t cpu_show_spectre_v2(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) + return sprintf(buf, "Mitigation: execute trampolines\n"); + if (__test_facility(82, S390_lowcore.alt_stfle_fac_list)) + return sprintf(buf, "Mitigation: limited branch prediction\n"); + return sprintf(buf, "Vulnerable\n"); +} diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 1c9ddd7aa5ec8f..0292d68e7dded7 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -753,6 +753,10 @@ static int __hw_perf_event_init(struct perf_event *event) */ rate = 0; if (attr->freq) { + if (!attr->sample_freq) { + err = -EINVAL; + goto out; + } rate = freq_to_sample_rate(&si, attr->sample_freq); rate = hw_limit_rate(&si, rate); attr->freq = 0; diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S index 73cc3750f0d341..7f14adf512c6d2 100644 --- a/arch/s390/kernel/reipl.S +++ b/arch/s390/kernel/reipl.S @@ -7,8 +7,11 @@ #include #include +#include #include + GEN_BR_THUNK %r9 + # # Issue "store status" for the current CPU to its prefix page # and call passed function afterwards @@ -67,9 +70,9 @@ ENTRY(store_status) st %r4,0(%r1) st %r5,4(%r1) stg %r2,8(%r1) - lgr %r1,%r2 + lgr %r9,%r2 lgr %r2,%r3 - br %r1 + BR_EX %r9 .section .bss .align 8 diff --git a/arch/s390/kernel/swsusp.S b/arch/s390/kernel/swsusp.S index e99187149f1717..a049a7b9d6e893 100644 --- a/arch/s390/kernel/swsusp.S +++ b/arch/s390/kernel/swsusp.S @@ -13,6 +13,7 @@ #include #include #include +#include #include /* @@ -24,6 +25,8 @@ * (see below) in the resume process. * This function runs with disabled interrupts. */ + GEN_BR_THUNK %r14 + .section .text ENTRY(swsusp_arch_suspend) stmg %r6,%r15,__SF_GPRS(%r15) @@ -103,7 +106,7 @@ ENTRY(swsusp_arch_suspend) spx 0x318(%r1) lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15) lghi %r2,0 - br %r14 + BR_EX %r14 /* * Restore saved memory image to correct place and restore register context. @@ -197,11 +200,10 @@ pgm_check_entry: larl %r15,init_thread_union ahi %r15,1<<(PAGE_SHIFT+THREAD_SIZE_ORDER) larl %r2,.Lpanic_string - larl %r3,sclp_early_printk lghi %r1,0 sam31 sigp %r1,%r0,SIGP_SET_ARCHITECTURE - basr %r14,%r3 + brasl %r14,sclp_early_printk larl %r3,.Ldisabled_wait_31 lpsw 0(%r3) 4: @@ -267,7 +269,7 @@ restore_registers: /* Return 0 */ lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15) lghi %r2,0 - br %r14 + BR_EX %r14 .section .data..nosave,"aw",@progbits .align 8 diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index 8961e3970901d4..969882b542669b 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -578,7 +578,7 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) gpa = READ_ONCE(scb_o->itdba) & ~0xffUL; if (gpa && (scb_s->ecb & ECB_TE)) { - if (!(gpa & ~0x1fffU)) { + if (!(gpa & ~0x1fffUL)) { rc = set_validity_icpt(scb_s, 0x0080U); goto unpin; } diff --git a/arch/s390/lib/mem.S b/arch/s390/lib/mem.S index 495c9c4bacc7b3..2311f15be9cf04 100644 --- a/arch/s390/lib/mem.S +++ b/arch/s390/lib/mem.S @@ -7,6 +7,9 @@ #include #include +#include + + GEN_BR_THUNK %r14 /* * void *memmove(void *dest, const void *src, size_t n) @@ -33,14 +36,14 @@ ENTRY(memmove) .Lmemmove_forward_remainder: larl %r5,.Lmemmove_mvc ex %r4,0(%r5) - br %r14 + BR_EX %r14 .Lmemmove_reverse: ic %r0,0(%r4,%r3) stc %r0,0(%r4,%r1) brctg %r4,.Lmemmove_reverse ic %r0,0(%r4,%r3) stc %r0,0(%r4,%r1) - br %r14 + BR_EX %r14 .Lmemmove_mvc: mvc 0(1,%r1),0(%r3) EXPORT_SYMBOL(memmove) @@ -77,7 +80,7 @@ ENTRY(memset) .Lmemset_clear_remainder: larl %r3,.Lmemset_xc ex %r4,0(%r3) - br %r14 + BR_EX %r14 .Lmemset_fill: cghi %r4,1 lgr %r1,%r2 @@ -95,10 +98,10 @@ ENTRY(memset) stc %r3,0(%r1) larl %r5,.Lmemset_mvc ex %r4,0(%r5) - br %r14 + BR_EX %r14 .Lmemset_fill_exit: stc %r3,0(%r1) - br %r14 + BR_EX %r14 .Lmemset_xc: xc 0(1,%r1),0(%r1) .Lmemset_mvc: @@ -121,7 +124,7 @@ ENTRY(memcpy) .Lmemcpy_remainder: larl %r5,.Lmemcpy_mvc ex %r4,0(%r5) - br %r14 + BR_EX %r14 .Lmemcpy_loop: mvc 0(256,%r1),0(%r3) la %r1,256(%r1) @@ -159,10 +162,10 @@ ENTRY(__memset\bits) \insn %r3,0(%r1) larl %r5,.L__memset_mvc\bits ex %r4,0(%r5) - br %r14 + BR_EX %r14 .L__memset_exit\bits: \insn %r3,0(%r2) - br %r14 + BR_EX %r14 .L__memset_mvc\bits: mvc \bytes(1,%r1),0(%r1) .endm diff --git a/arch/s390/net/bpf_jit.S b/arch/s390/net/bpf_jit.S index 25bb4643c4f46c..9f794869c1b090 100644 --- a/arch/s390/net/bpf_jit.S +++ b/arch/s390/net/bpf_jit.S @@ -9,6 +9,7 @@ */ #include +#include #include "bpf_jit.h" /* @@ -54,7 +55,7 @@ ENTRY(sk_load_##NAME##_pos); \ clg %r3,STK_OFF_HLEN(%r15); /* Offset + SIZE > hlen? */ \ jh sk_load_##NAME##_slow; \ LOAD %r14,-SIZE(%r3,%r12); /* Get data from skb */ \ - b OFF_OK(%r6); /* Return */ \ + B_EX OFF_OK,%r6; /* Return */ \ \ sk_load_##NAME##_slow:; \ lgr %r2,%r7; /* Arg1 = skb pointer */ \ @@ -64,11 +65,14 @@ sk_load_##NAME##_slow:; \ brasl %r14,skb_copy_bits; /* Get data from skb */ \ LOAD %r14,STK_OFF_TMP(%r15); /* Load from temp bufffer */ \ ltgr %r2,%r2; /* Set cc to (%r2 != 0) */ \ - br %r6; /* Return */ + BR_EX %r6; /* Return */ sk_load_common(word, 4, llgf) /* r14 = *(u32 *) (skb->data+offset) */ sk_load_common(half, 2, llgh) /* r14 = *(u16 *) (skb->data+offset) */ + GEN_BR_THUNK %r6 + GEN_B_THUNK OFF_OK,%r6 + /* * Load 1 byte from SKB (optimized version) */ @@ -80,7 +84,7 @@ ENTRY(sk_load_byte_pos) clg %r3,STK_OFF_HLEN(%r15) # Offset >= hlen? jnl sk_load_byte_slow llgc %r14,0(%r3,%r12) # Get byte from skb - b OFF_OK(%r6) # Return OK + B_EX OFF_OK,%r6 # Return OK sk_load_byte_slow: lgr %r2,%r7 # Arg1 = skb pointer @@ -90,7 +94,7 @@ sk_load_byte_slow: brasl %r14,skb_copy_bits # Get data from skb llgc %r14,STK_OFF_TMP(%r15) # Load result from temp buffer ltgr %r2,%r2 # Set cc to (%r2 != 0) - br %r6 # Return cc + BR_EX %r6 # Return cc #define sk_negative_common(NAME, SIZE, LOAD) \ sk_load_##NAME##_slow_neg:; \ @@ -104,7 +108,7 @@ sk_load_##NAME##_slow_neg:; \ jz bpf_error; \ LOAD %r14,0(%r2); /* Get data from pointer */ \ xr %r3,%r3; /* Set cc to zero */ \ - br %r6; /* Return cc */ + BR_EX %r6; /* Return cc */ sk_negative_common(word, 4, llgf) sk_negative_common(half, 2, llgh) @@ -113,4 +117,4 @@ sk_negative_common(byte, 1, llgc) bpf_error: # force a return 0 from jit handler ltgr %r15,%r15 # Set condition code - br %r6 + BR_EX %r6 diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index 78a19c93b3802f..dd2bcf0e7d00d4 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include "bpf_jit.h" @@ -41,6 +43,8 @@ struct bpf_jit { int base_ip; /* Base address for literal pool */ int ret0_ip; /* Address of return 0 */ int exit_ip; /* Address of exit */ + int r1_thunk_ip; /* Address of expoline thunk for 'br %r1' */ + int r14_thunk_ip; /* Address of expoline thunk for 'br %r14' */ int tail_call_start; /* Tail call start offset */ int labels[1]; /* Labels for local jumps */ }; @@ -250,6 +254,19 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1) REG_SET_SEEN(b2); \ }) +#define EMIT6_PCREL_RILB(op, b, target) \ +({ \ + int rel = (target - jit->prg) / 2; \ + _EMIT6(op | reg_high(b) << 16 | rel >> 16, rel & 0xffff); \ + REG_SET_SEEN(b); \ +}) + +#define EMIT6_PCREL_RIL(op, target) \ +({ \ + int rel = (target - jit->prg) / 2; \ + _EMIT6(op | rel >> 16, rel & 0xffff); \ +}) + #define _EMIT6_IMM(op, imm) \ ({ \ unsigned int __imm = (imm); \ @@ -469,8 +486,45 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth) EMIT4(0xb9040000, REG_2, BPF_REG_0); /* Restore registers */ save_restore_regs(jit, REGS_RESTORE, stack_depth); + if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) { + jit->r14_thunk_ip = jit->prg; + /* Generate __s390_indirect_jump_r14 thunk */ + if (test_facility(35)) { + /* exrl %r0,.+10 */ + EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10); + } else { + /* larl %r1,.+14 */ + EMIT6_PCREL_RILB(0xc0000000, REG_1, jit->prg + 14); + /* ex 0,0(%r1) */ + EMIT4_DISP(0x44000000, REG_0, REG_1, 0); + } + /* j . */ + EMIT4_PCREL(0xa7f40000, 0); + } /* br %r14 */ _EMIT2(0x07fe); + + if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable && + (jit->seen & SEEN_FUNC)) { + jit->r1_thunk_ip = jit->prg; + /* Generate __s390_indirect_jump_r1 thunk */ + if (test_facility(35)) { + /* exrl %r0,.+10 */ + EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10); + /* j . */ + EMIT4_PCREL(0xa7f40000, 0); + /* br %r1 */ + _EMIT2(0x07f1); + } else { + /* larl %r1,.+14 */ + EMIT6_PCREL_RILB(0xc0000000, REG_1, jit->prg + 14); + /* ex 0,S390_lowcore.br_r1_tampoline */ + EMIT4_DISP(0x44000000, REG_0, REG_0, + offsetof(struct lowcore, br_r1_trampoline)); + /* j . */ + EMIT4_PCREL(0xa7f40000, 0); + } + } } /* @@ -966,8 +1020,13 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i /* lg %w1,(%l) */ EMIT6_DISP_LH(0xe3000000, 0x0004, REG_W1, REG_0, REG_L, EMIT_CONST_U64(func)); - /* basr %r14,%w1 */ - EMIT2(0x0d00, REG_14, REG_W1); + if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable) { + /* brasl %r14,__s390_indirect_jump_r1 */ + EMIT6_PCREL_RILB(0xc0050000, REG_14, jit->r1_thunk_ip); + } else { + /* basr %r14,%w1 */ + EMIT2(0x0d00, REG_14, REG_W1); + } /* lgr %b0,%r2: load return value into %b0 */ EMIT4(0xb9040000, BPF_REG_0, REG_2); if ((jit->seen & SEEN_SKB) && diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile index e9525bc1b4a617..1ace023cbdcec3 100644 --- a/arch/s390/purgatory/Makefile +++ b/arch/s390/purgatory/Makefile @@ -21,7 +21,7 @@ LDFLAGS_purgatory.ro += -z nodefaultlib KBUILD_CFLAGS := -fno-strict-aliasing -Wall -Wstrict-prototypes KBUILD_CFLAGS += -Wno-pointer-sign -Wno-sign-compare KBUILD_CFLAGS += -fno-zero-initialized-in-bss -fno-builtin -ffreestanding -KBUILD_CFLAGS += -c -MD -Os -m64 +KBUILD_CFLAGS += -c -MD -Os -m64 -msoft-float KBUILD_CFLAGS += $(call cc-option,-fno-PIE) $(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 97fe2931647648..1851eaeee13179 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -9,6 +9,7 @@ config SUPERH select HAVE_IDE if HAS_IOPORT_MAP select HAVE_MEMBLOCK select HAVE_MEMBLOCK_NODE_MAP + select NO_BOOTMEM select ARCH_DISCARD_MEMBLOCK select HAVE_OPROFILE select HAVE_GENERIC_DMA_COHERENT diff --git a/arch/sh/kernel/cpu/sh2/probe.c b/arch/sh/kernel/cpu/sh2/probe.c index 4205f6d42b6938..a5bd0364267890 100644 --- a/arch/sh/kernel/cpu/sh2/probe.c +++ b/arch/sh/kernel/cpu/sh2/probe.c @@ -43,7 +43,11 @@ void __ref cpu_probe(void) #endif #if defined(CONFIG_CPU_J2) +#if defined(CONFIG_SMP) unsigned cpu = hard_smp_processor_id(); +#else + unsigned cpu = 0; +#endif if (cpu == 0) of_scan_flat_dt(scan_cache, NULL); if (j2_ccr_base) __raw_writel(0x80000303, j2_ccr_base + 4*cpu); if (cpu != 0) return; diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index d34e998b809f34..c286cf5da6e770 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c index 8ce98691d82257..f1b44697ad6802 100644 --- a/arch/sh/mm/consistent.c +++ b/arch/sh/mm/consistent.c @@ -59,7 +59,9 @@ void *dma_generic_alloc_coherent(struct device *dev, size_t size, split_page(pfn_to_page(virt_to_phys(ret) >> PAGE_SHIFT), order); - *dma_handle = virt_to_phys(ret) - PFN_PHYS(dev->dma_pfn_offset); + *dma_handle = virt_to_phys(ret); + if (!WARN_ON(!dev)) + *dma_handle -= PFN_PHYS(dev->dma_pfn_offset); return ret_nocache; } @@ -69,9 +71,12 @@ void dma_generic_free_coherent(struct device *dev, size_t size, unsigned long attrs) { int order = get_order(size); - unsigned long pfn = (dma_handle >> PAGE_SHIFT) + dev->dma_pfn_offset; + unsigned long pfn = dma_handle >> PAGE_SHIFT; int k; + if (!WARN_ON(!dev)) + pfn += dev->dma_pfn_offset; + for (k = 0; k < (1 << order); k++) __free_pages(pfn_to_page(pfn + k), 0); @@ -143,7 +148,7 @@ int __init platform_resource_setup_memory(struct platform_device *pdev, if (!memsize) return 0; - buf = dma_alloc_coherent(NULL, memsize, &dma_handle, GFP_KERNEL); + buf = dma_alloc_coherent(&pdev->dev, memsize, &dma_handle, GFP_KERNEL); if (!buf) { pr_warning("%s: unable to allocate memory\n", name); return -ENOMEM; diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index ce0bbaa7e40403..4034035fbede8a 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -211,59 +211,15 @@ void __init allocate_pgdat(unsigned int nid) NODE_DATA(nid) = __va(phys); memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); - - NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; #endif NODE_DATA(nid)->node_start_pfn = start_pfn; NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn; } -static void __init bootmem_init_one_node(unsigned int nid) -{ - unsigned long total_pages, paddr; - unsigned long end_pfn; - struct pglist_data *p; - - p = NODE_DATA(nid); - - /* Nothing to do.. */ - if (!p->node_spanned_pages) - return; - - end_pfn = pgdat_end_pfn(p); - - total_pages = bootmem_bootmap_pages(p->node_spanned_pages); - - paddr = memblock_alloc(total_pages << PAGE_SHIFT, PAGE_SIZE); - if (!paddr) - panic("Can't allocate bootmap for nid[%d]\n", nid); - - init_bootmem_node(p, paddr >> PAGE_SHIFT, p->node_start_pfn, end_pfn); - - free_bootmem_with_active_regions(nid, end_pfn); - - /* - * XXX Handle initial reservations for the system memory node - * only for the moment, we'll refactor this later for handling - * reservations in other nodes. - */ - if (nid == 0) { - struct memblock_region *reg; - - /* Reserve the sections we're already using. */ - for_each_memblock(reserved, reg) { - reserve_bootmem(reg->base, reg->size, BOOTMEM_DEFAULT); - } - } - - sparse_memory_present_with_active_regions(nid); -} - static void __init do_init_bootmem(void) { struct memblock_region *reg; - int i; /* Add active regions with valid PFNs. */ for_each_memblock(memory, reg) { @@ -279,9 +235,12 @@ static void __init do_init_bootmem(void) plat_mem_setup(); - for_each_online_node(i) - bootmem_init_one_node(i); + for_each_memblock(memory, reg) { + int nid = memblock_get_region_node(reg); + memory_present(nid, memblock_region_memory_base_pfn(reg), + memblock_region_memory_end_pfn(reg)); + } sparse_init(); } @@ -322,7 +281,6 @@ void __init paging_init(void) { unsigned long max_zone_pfns[MAX_NR_ZONES]; unsigned long vaddr, end; - int nid; sh_mv.mv_mem_init(); @@ -377,21 +335,7 @@ void __init paging_init(void) kmap_coherent_init(); memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); - - for_each_online_node(nid) { - pg_data_t *pgdat = NODE_DATA(nid); - unsigned long low, start_pfn; - - start_pfn = pgdat->bdata->node_min_pfn; - low = pgdat->bdata->node_low_pfn; - - if (max_zone_pfns[ZONE_NORMAL] < low) - max_zone_pfns[ZONE_NORMAL] = low; - - printk("Node %u: start_pfn = 0x%lx, low = 0x%lx\n", - nid, start_pfn, low); - } - + max_zone_pfns[ZONE_NORMAL] = max_low_pfn; free_area_init_nodes(max_zone_pfns); } diff --git a/arch/sh/mm/numa.c b/arch/sh/mm/numa.c index 05713d190247c6..830e8b3684e424 100644 --- a/arch/sh/mm/numa.c +++ b/arch/sh/mm/numa.c @@ -8,7 +8,6 @@ * for more details. */ #include -#include #include #include #include @@ -26,9 +25,7 @@ EXPORT_SYMBOL_GPL(node_data); */ void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end) { - unsigned long bootmap_pages; unsigned long start_pfn, end_pfn; - unsigned long bootmem_paddr; /* Don't allow bogus node assignment */ BUG_ON(nid >= MAX_NUMNODES || nid <= 0); @@ -48,25 +45,9 @@ void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end) SMP_CACHE_BYTES, end)); memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); - NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; NODE_DATA(nid)->node_start_pfn = start_pfn; NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn; - /* Node-local bootmap */ - bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn); - bootmem_paddr = memblock_alloc_base(bootmap_pages << PAGE_SHIFT, - PAGE_SIZE, end); - init_bootmem_node(NODE_DATA(nid), bootmem_paddr >> PAGE_SHIFT, - start_pfn, end_pfn); - - free_bootmem_with_active_regions(nid, end_pfn); - - /* Reserve the pgdat and bootmap space with the bootmem allocator */ - reserve_bootmem_node(NODE_DATA(nid), start_pfn << PAGE_SHIFT, - sizeof(struct pglist_data), BOOTMEM_DEFAULT); - reserve_bootmem_node(NODE_DATA(nid), bootmem_paddr, - bootmap_pages << PAGE_SHIFT, BOOTMEM_DEFAULT); - /* It's up */ node_set_online(nid); diff --git a/arch/um/drivers/vector_transports.c b/arch/um/drivers/vector_transports.c index 9065047f844bae..77e4ebc206ae1d 100644 --- a/arch/um/drivers/vector_transports.c +++ b/arch/um/drivers/vector_transports.c @@ -120,7 +120,8 @@ static int raw_form_header(uint8_t *header, skb, vheader, virtio_legacy_is_little_endian(), - false + false, + 0 ); return 0; diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 47d3efff6805b9..09f36c0d9d4fe1 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -163,7 +163,8 @@ __setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom) if (status != EFI_SUCCESS) goto free_struct; - memcpy(rom->romdata, pci->romimage, pci->romsize); + memcpy(rom->romdata, (void *)(unsigned long)pci->romimage, + pci->romsize); return status; free_struct: @@ -269,7 +270,8 @@ __setup_efi_pci64(efi_pci_io_protocol_64 *pci, struct pci_setup_rom **__rom) if (status != EFI_SUCCESS) goto free_struct; - memcpy(rom->romdata, pci->romimage, pci->romsize); + memcpy(rom->romdata, (void *)(unsigned long)pci->romimage, + pci->romsize); return status; free_struct: diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index fca012baba19ff..8169e8b7a4dc12 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -305,6 +305,25 @@ ENTRY(startup_64) /* Set up the stack */ leaq boot_stack_end(%rbx), %rsp + /* + * paging_prepare() and cleanup_trampoline() below can have GOT + * references. Adjust the table with address we are running at. + * + * Zero RAX for adjust_got: the GOT was not adjusted before; + * there's no adjustment to undo. + */ + xorq %rax, %rax + + /* + * Calculate the address the binary is loaded at and use it as + * a GOT adjustment. + */ + call 1f +1: popq %rdi + subq $1b, %rdi + + call adjust_got + /* * At this point we are in long mode with 4-level paging enabled, * but we might want to enable 5-level paging or vice versa. @@ -370,10 +389,14 @@ trampoline_return: /* * cleanup_trampoline() would restore trampoline memory. * + * RDI is address of the page table to use instead of page table + * in trampoline memory (if required). + * * RSI holds real mode data and needs to be preserved across * this function call. */ pushq %rsi + leaq top_pgtable(%rbx), %rdi call cleanup_trampoline popq %rsi @@ -381,6 +404,21 @@ trampoline_return: pushq $0 popfq + /* + * Previously we've adjusted the GOT with address the binary was + * loaded at. Now we need to re-adjust for relocation address. + * + * Calculate the address the binary is loaded at, so that we can + * undo the previous GOT adjustment. + */ + call 1f +1: popq %rax + subq $1b, %rax + + /* The new adjustment is the relocation address */ + movq %rbx, %rdi + call adjust_got + /* * Copy the compressed kernel to the end of our buffer * where decompression in place becomes safe. @@ -481,19 +519,6 @@ relocated: shrq $3, %rcx rep stosq -/* - * Adjust our own GOT - */ - leaq _got(%rip), %rdx - leaq _egot(%rip), %rcx -1: - cmpq %rcx, %rdx - jae 2f - addq %rbx, (%rdx) - addq $8, %rdx - jmp 1b -2: - /* * Do the extraction, and jump to the new kernel.. */ @@ -512,6 +537,27 @@ relocated: */ jmp *%rax +/* + * Adjust the global offset table + * + * RAX is the previous adjustment of the table to undo (use 0 if it's the + * first time we touch GOT). + * RDI is the new adjustment to apply. + */ +adjust_got: + /* Walk through the GOT adding the address to the entries */ + leaq _got(%rip), %rdx + leaq _egot(%rip), %rcx +1: + cmpq %rcx, %rdx + jae 2f + subq %rax, (%rdx) /* Undo previous adjustment */ + addq %rdi, (%rdx) /* Apply the new adjustment */ + addq $8, %rdx + jmp 1b +2: + ret + .code32 /* * This is the 32-bit trampoline that will be copied over to low memory. @@ -649,3 +695,10 @@ boot_stack_end: .balign 4096 pgtable: .fill BOOT_PGT_SIZE, 1, 0 + +/* + * The page table is going to be used instead of page table in the trampoline + * memory. + */ +top_pgtable: + .fill PAGE_SIZE, 1, 0 diff --git a/arch/x86/boot/compressed/pgtable_64.c b/arch/x86/boot/compressed/pgtable_64.c index 32af1cbcd9030f..a362fa0b849c70 100644 --- a/arch/x86/boot/compressed/pgtable_64.c +++ b/arch/x86/boot/compressed/pgtable_64.c @@ -22,14 +22,6 @@ struct paging_config { /* Buffer to preserve trampoline memory */ static char trampoline_save[TRAMPOLINE_32BIT_SIZE]; -/* - * The page table is going to be used instead of page table in the trampoline - * memory. - * - * It must not be in BSS as BSS is cleared after cleanup_trampoline(). - */ -static char top_pgtable[PAGE_SIZE] __aligned(PAGE_SIZE) __section(.data); - /* * Trampoline address will be printed by extract_kernel() for debugging * purposes. @@ -134,7 +126,7 @@ struct paging_config paging_prepare(void) return paging_config; } -void cleanup_trampoline(void) +void cleanup_trampoline(void *pgtable) { void *trampoline_pgtable; @@ -145,8 +137,8 @@ void cleanup_trampoline(void) * if it's there. */ if ((void *)__native_read_cr3() == trampoline_pgtable) { - memcpy(top_pgtable, trampoline_pgtable, PAGE_SIZE); - native_write_cr3((unsigned long)top_pgtable); + memcpy(pgtable, trampoline_pgtable, PAGE_SIZE); + native_write_cr3((unsigned long)pgtable); } /* Restore trampoline memory */ diff --git a/arch/x86/entry/vdso/vdso32/vdso-fakesections.c b/arch/x86/entry/vdso/vdso32/vdso-fakesections.c deleted file mode 100644 index 541468e25265bf..00000000000000 --- a/arch/x86/entry/vdso/vdso32/vdso-fakesections.c +++ /dev/null @@ -1 +0,0 @@ -#include "../vdso-fakesections.c" diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index a6006e7bb729a8..45b2b1c93d0422 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -304,17 +305,20 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event) config = attr->config; - cache_type = (config >> 0) & 0xff; + cache_type = (config >> 0) & 0xff; if (cache_type >= PERF_COUNT_HW_CACHE_MAX) return -EINVAL; + cache_type = array_index_nospec(cache_type, PERF_COUNT_HW_CACHE_MAX); cache_op = (config >> 8) & 0xff; if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX) return -EINVAL; + cache_op = array_index_nospec(cache_op, PERF_COUNT_HW_CACHE_OP_MAX); cache_result = (config >> 16) & 0xff; if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) return -EINVAL; + cache_result = array_index_nospec(cache_result, PERF_COUNT_HW_CACHE_RESULT_MAX); val = hw_cache_event_ids[cache_type][cache_op][cache_result]; @@ -421,6 +425,8 @@ int x86_setup_perfctr(struct perf_event *event) if (attr->config >= x86_pmu.max_events) return -EINVAL; + attr->config = array_index_nospec((unsigned long)attr->config, x86_pmu.max_events); + /* * The generic map: */ diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c index 9aca448bb8e633..9f8084f18d58e4 100644 --- a/arch/x86/events/intel/cstate.c +++ b/arch/x86/events/intel/cstate.c @@ -92,6 +92,7 @@ #include #include #include +#include #include #include #include "../perf_event.h" @@ -302,6 +303,7 @@ static int cstate_pmu_event_init(struct perf_event *event) } else if (event->pmu == &cstate_pkg_pmu) { if (cfg >= PERF_CSTATE_PKG_EVENT_MAX) return -EINVAL; + cfg = array_index_nospec((unsigned long)cfg, PERF_CSTATE_PKG_EVENT_MAX); if (!pkg_msr[cfg].attr) return -EINVAL; event->hw.event_base = pkg_msr[cfg].msr; diff --git a/arch/x86/events/msr.c b/arch/x86/events/msr.c index e7edf19e64c27c..b4771a6ddbc1b6 100644 --- a/arch/x86/events/msr.c +++ b/arch/x86/events/msr.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include enum perf_msr_id { @@ -158,9 +159,6 @@ static int msr_event_init(struct perf_event *event) if (event->attr.type != event->pmu->type) return -ENOENT; - if (cfg >= PERF_MSR_EVENT_MAX) - return -EINVAL; - /* unsupported modes and filters */ if (event->attr.exclude_user || event->attr.exclude_kernel || @@ -171,6 +169,11 @@ static int msr_event_init(struct perf_event *event) event->attr.sample_period) /* no sampling */ return -EINVAL; + if (cfg >= PERF_MSR_EVENT_MAX) + return -EINVAL; + + cfg = array_index_nospec((unsigned long)cfg, PERF_MSR_EVENT_MAX); + if (!msr[cfg].attr) return -EINVAL; diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h index 08acd954f00e2d..74a9e06b6cfd56 100644 --- a/arch/x86/include/asm/apic.h +++ b/arch/x86/include/asm/apic.h @@ -436,6 +436,8 @@ static inline void apic_set_eoi_write(void (*eoi_write)(u32 reg, u32 v)) {} #endif /* CONFIG_X86_LOCAL_APIC */ +extern void apic_ack_irq(struct irq_data *data); + static inline void ack_APIC_irq(void) { /* diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index b27da9602a6dfd..aced6c9290d6f9 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h @@ -140,6 +140,20 @@ extern void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int bit); #define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit) +#if defined(__clang__) && !defined(CC_HAVE_ASM_GOTO) + +/* + * Workaround for the sake of BPF compilation which utilizes kernel + * headers, but clang does not support ASM GOTO and fails the build. + */ +#ifndef __BPF_TRACING__ +#warning "Compiler lacks ASM_GOTO support. Add -D __BPF_TRACING__ to your compiler arguments" +#endif + +#define static_cpu_has(bit) boot_cpu_has(bit) + +#else + /* * Static testing of CPU features. Used the same as boot_cpu_has(). * These will statically patch the target code for additional @@ -195,6 +209,7 @@ static __always_inline __pure bool _static_cpu_has(u16 bit) boot_cpu_has(bit) : \ _static_cpu_has(bit) \ ) +#endif #define cpu_has_bug(c, bit) cpu_has(c, (bit)) #define set_cpu_bug(c, bit) set_cpu_cap(c, (bit)) diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index 578793e97431da..fb00a2fca9901e 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -198,7 +198,6 @@ #define X86_FEATURE_CAT_L2 ( 7*32+ 5) /* Cache Allocation Technology L2 */ #define X86_FEATURE_CDP_L3 ( 7*32+ 6) /* Code and Data Prioritization L3 */ #define X86_FEATURE_INVPCID_SINGLE ( 7*32+ 7) /* Effectively INVPCID && CR4.PCIDE=1 */ - #define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */ #define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */ #define X86_FEATURE_SME ( 7*32+10) /* AMD Secure Memory Encryption */ @@ -207,13 +206,19 @@ #define X86_FEATURE_RETPOLINE_AMD ( 7*32+13) /* "" AMD Retpoline mitigation for Spectre variant 2 */ #define X86_FEATURE_INTEL_PPIN ( 7*32+14) /* Intel Processor Inventory Number */ #define X86_FEATURE_CDP_L2 ( 7*32+15) /* Code and Data Prioritization L2 */ - +#define X86_FEATURE_MSR_SPEC_CTRL ( 7*32+16) /* "" MSR SPEC_CTRL is implemented */ +#define X86_FEATURE_SSBD ( 7*32+17) /* Speculative Store Bypass Disable */ #define X86_FEATURE_MBA ( 7*32+18) /* Memory Bandwidth Allocation */ #define X86_FEATURE_RSB_CTXSW ( 7*32+19) /* "" Fill RSB on context switches */ #define X86_FEATURE_SEV ( 7*32+20) /* AMD Secure Encrypted Virtualization */ - #define X86_FEATURE_USE_IBPB ( 7*32+21) /* "" Indirect Branch Prediction Barrier enabled */ #define X86_FEATURE_USE_IBRS_FW ( 7*32+22) /* "" Use IBRS during runtime firmware calls */ +#define X86_FEATURE_SPEC_STORE_BYPASS_DISABLE ( 7*32+23) /* "" Disable Speculative Store Bypass. */ +#define X86_FEATURE_LS_CFG_SSBD ( 7*32+24) /* "" AMD SSBD implementation via LS_CFG MSR */ +#define X86_FEATURE_IBRS ( 7*32+25) /* Indirect Branch Restricted Speculation */ +#define X86_FEATURE_IBPB ( 7*32+26) /* Indirect Branch Prediction Barrier */ +#define X86_FEATURE_STIBP ( 7*32+27) /* Single Thread Indirect Branch Predictors */ +#define X86_FEATURE_ZEN ( 7*32+28) /* "" CPU is AMD family 0x17 (Zen) */ /* Virtualization flags: Linux defined, word 8 */ #define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */ @@ -274,9 +279,10 @@ #define X86_FEATURE_CLZERO (13*32+ 0) /* CLZERO instruction */ #define X86_FEATURE_IRPERF (13*32+ 1) /* Instructions Retired Count */ #define X86_FEATURE_XSAVEERPTR (13*32+ 2) /* Always save/restore FP error pointers */ -#define X86_FEATURE_IBPB (13*32+12) /* Indirect Branch Prediction Barrier */ -#define X86_FEATURE_IBRS (13*32+14) /* Indirect Branch Restricted Speculation */ -#define X86_FEATURE_STIBP (13*32+15) /* Single Thread Indirect Branch Predictors */ +#define X86_FEATURE_AMD_IBPB (13*32+12) /* "" Indirect Branch Prediction Barrier */ +#define X86_FEATURE_AMD_IBRS (13*32+14) /* "" Indirect Branch Restricted Speculation */ +#define X86_FEATURE_AMD_STIBP (13*32+15) /* "" Single Thread Indirect Branch Predictors */ +#define X86_FEATURE_VIRT_SSBD (13*32+25) /* Virtualized Speculative Store Bypass Disable */ /* Thermal and Power Management Leaf, CPUID level 0x00000006 (EAX), word 14 */ #define X86_FEATURE_DTHERM (14*32+ 0) /* Digital Thermal Sensor */ @@ -334,6 +340,7 @@ #define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */ #define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */ #define X86_FEATURE_ARCH_CAPABILITIES (18*32+29) /* IA32_ARCH_CAPABILITIES MSR (Intel) */ +#define X86_FEATURE_SPEC_CTRL_SSBD (18*32+31) /* "" Speculative Store Bypass Disable */ /* * BUG word(s) @@ -363,5 +370,6 @@ #define X86_BUG_CPU_MELTDOWN X86_BUG(14) /* CPU is affected by meltdown attack and needs kernel page table isolation */ #define X86_BUG_SPECTRE_V1 X86_BUG(15) /* CPU is affected by Spectre variant 1 attack with conditional branches */ #define X86_BUG_SPECTRE_V2 X86_BUG(16) /* CPU is affected by Spectre variant 2 attack with indirect branches */ +#define X86_BUG_SPEC_STORE_BYPASS X86_BUG(17) /* CPU is affected by speculative store bypass attack */ #endif /* _ASM_X86_CPUFEATURES_H */ diff --git a/arch/x86/include/asm/insn.h b/arch/x86/include/asm/insn.h index b3e32b010ab194..c2c01f84df75f1 100644 --- a/arch/x86/include/asm/insn.h +++ b/arch/x86/include/asm/insn.h @@ -208,4 +208,22 @@ static inline int insn_offset_immediate(struct insn *insn) return insn_offset_displacement(insn) + insn->displacement.nbytes; } +#define POP_SS_OPCODE 0x1f +#define MOV_SREG_OPCODE 0x8e + +/* + * Intel SDM Vol.3A 6.8.3 states; + * "Any single-step trap that would be delivered following the MOV to SS + * instruction or POP to SS instruction (because EFLAGS.TF is 1) is + * suppressed." + * This function returns true if @insn is MOV SS or POP SS. On these + * instructions, single stepping is suppressed. + */ +static inline int insn_masking_exception(struct insn *insn) +{ + return insn->opcode.bytes[0] == POP_SS_OPCODE || + (insn->opcode.bytes[0] == MOV_SREG_OPCODE && + X86_MODRM_REG(insn->modrm.bytes[0]) == 2); +} + #endif /* _ASM_X86_INSN_H */ diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h index b24b1c8b397989..0f82cd91cd3c4d 100644 --- a/arch/x86/include/asm/kvm_emulate.h +++ b/arch/x86/include/asm/kvm_emulate.h @@ -107,11 +107,12 @@ struct x86_emulate_ops { * @addr: [IN ] Linear address from which to read. * @val: [OUT] Value read from memory, zero-extended to 'u_long'. * @bytes: [IN ] Number of bytes to read from memory. + * @system:[IN ] Whether the access is forced to be at CPL0. */ int (*read_std)(struct x86_emulate_ctxt *ctxt, unsigned long addr, void *val, unsigned int bytes, - struct x86_exception *fault); + struct x86_exception *fault, bool system); /* * read_phys: Read bytes of standard (non-emulated/special) memory. @@ -129,10 +130,11 @@ struct x86_emulate_ops { * @addr: [IN ] Linear address to which to write. * @val: [OUT] Value write to memory, zero-extended to 'u_long'. * @bytes: [IN ] Number of bytes to write to memory. + * @system:[IN ] Whether the access is forced to be at CPL0. */ int (*write_std)(struct x86_emulate_ctxt *ctxt, unsigned long addr, void *val, unsigned int bytes, - struct x86_exception *fault); + struct x86_exception *fault, bool system); /* * fetch: Read bytes of standard (non-emulated/special) memory. * Used for instruction fetch. diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index c25775fad4ed19..f4b2588865e9f7 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -924,7 +924,7 @@ struct kvm_x86_ops { int (*hardware_setup)(void); /* __init */ void (*hardware_unsetup)(void); /* __exit */ bool (*cpu_has_accelerated_tpr)(void); - bool (*cpu_has_high_real_mode_segbase)(void); + bool (*has_emulated_msr)(int index); void (*cpuid_update)(struct kvm_vcpu *vcpu); struct kvm *(*vm_alloc)(void); diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 57e3785d0d26e1..cf9911b5a53cb1 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -193,7 +193,7 @@ static inline int init_new_context(struct task_struct *tsk, #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS if (cpu_feature_enabled(X86_FEATURE_OSPKE)) { - /* pkey 0 is the default and always allocated */ + /* pkey 0 is the default and allocated implicitly */ mm->context.pkey_allocation_map = 0x1; /* -1 means unallocated or invalid */ mm->context.execute_only_pkey = -1; diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 53d5b1b9255eb8..fda2114197b369 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -42,6 +42,8 @@ #define MSR_IA32_SPEC_CTRL 0x00000048 /* Speculation Control */ #define SPEC_CTRL_IBRS (1 << 0) /* Indirect Branch Restricted Speculation */ #define SPEC_CTRL_STIBP (1 << 1) /* Single Thread Indirect Branch Predictors */ +#define SPEC_CTRL_SSBD_SHIFT 2 /* Speculative Store Bypass Disable bit */ +#define SPEC_CTRL_SSBD (1 << SPEC_CTRL_SSBD_SHIFT) /* Speculative Store Bypass Disable */ #define MSR_IA32_PRED_CMD 0x00000049 /* Prediction Command */ #define PRED_CMD_IBPB (1 << 0) /* Indirect Branch Prediction Barrier */ @@ -68,6 +70,11 @@ #define MSR_IA32_ARCH_CAPABILITIES 0x0000010a #define ARCH_CAP_RDCL_NO (1 << 0) /* Not susceptible to Meltdown */ #define ARCH_CAP_IBRS_ALL (1 << 1) /* Enhanced IBRS support */ +#define ARCH_CAP_SSB_NO (1 << 4) /* + * Not susceptible to Speculative Store Bypass + * attack, so no Speculative Store Bypass + * control required. + */ #define MSR_IA32_BBL_CR_CTL 0x00000119 #define MSR_IA32_BBL_CR_CTL3 0x0000011e @@ -340,6 +347,8 @@ #define MSR_AMD64_SEV_ENABLED_BIT 0 #define MSR_AMD64_SEV_ENABLED BIT_ULL(MSR_AMD64_SEV_ENABLED_BIT) +#define MSR_AMD64_VIRT_SPEC_CTRL 0xc001011f + /* Fam 17h MSRs */ #define MSR_F17H_IRPERF 0xc00000e9 diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index f928ad9b143fed..8b38df98548e8d 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -217,6 +217,14 @@ enum spectre_v2_mitigation { SPECTRE_V2_IBRS, }; +/* The Speculative Store Bypass disable variants */ +enum ssb_mitigation { + SPEC_STORE_BYPASS_NONE, + SPEC_STORE_BYPASS_DISABLE, + SPEC_STORE_BYPASS_PRCTL, + SPEC_STORE_BYPASS_SECCOMP, +}; + extern char __indirect_thunk_start[]; extern char __indirect_thunk_end[]; @@ -241,22 +249,27 @@ static inline void vmexit_fill_RSB(void) #endif } -#define alternative_msr_write(_msr, _val, _feature) \ - asm volatile(ALTERNATIVE("", \ - "movl %[msr], %%ecx\n\t" \ - "movl %[val], %%eax\n\t" \ - "movl $0, %%edx\n\t" \ - "wrmsr", \ - _feature) \ - : : [msr] "i" (_msr), [val] "i" (_val) \ - : "eax", "ecx", "edx", "memory") +static __always_inline +void alternative_msr_write(unsigned int msr, u64 val, unsigned int feature) +{ + asm volatile(ALTERNATIVE("", "wrmsr", %c[feature]) + : : "c" (msr), + "a" ((u32)val), + "d" ((u32)(val >> 32)), + [feature] "i" (feature) + : "memory"); +} static inline void indirect_branch_prediction_barrier(void) { - alternative_msr_write(MSR_IA32_PRED_CMD, PRED_CMD_IBPB, - X86_FEATURE_USE_IBPB); + u64 val = PRED_CMD_IBPB; + + alternative_msr_write(MSR_IA32_PRED_CMD, val, X86_FEATURE_USE_IBPB); } +/* The Intel SPEC CTRL MSR base value cache */ +extern u64 x86_spec_ctrl_base; + /* * With retpoline, we must use IBRS to restrict branch prediction * before calling into firmware. @@ -265,14 +278,18 @@ static inline void indirect_branch_prediction_barrier(void) */ #define firmware_restrict_branch_speculation_start() \ do { \ + u64 val = x86_spec_ctrl_base | SPEC_CTRL_IBRS; \ + \ preempt_disable(); \ - alternative_msr_write(MSR_IA32_SPEC_CTRL, SPEC_CTRL_IBRS, \ + alternative_msr_write(MSR_IA32_SPEC_CTRL, val, \ X86_FEATURE_USE_IBRS_FW); \ } while (0) #define firmware_restrict_branch_speculation_end() \ do { \ - alternative_msr_write(MSR_IA32_SPEC_CTRL, 0, \ + u64 val = x86_spec_ctrl_base; \ + \ + alternative_msr_write(MSR_IA32_SPEC_CTRL, val, \ X86_FEATURE_USE_IBRS_FW); \ preempt_enable(); \ } while (0) diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h index a0ba1ffda0dfd3..851c04b7a0922c 100644 --- a/arch/x86/include/asm/pkeys.h +++ b/arch/x86/include/asm/pkeys.h @@ -2,6 +2,8 @@ #ifndef _ASM_X86_PKEYS_H #define _ASM_X86_PKEYS_H +#define ARCH_DEFAULT_PKEY 0 + #define arch_max_pkey() (boot_cpu_has(X86_FEATURE_OSPKE) ? 16 : 1) extern int arch_set_user_pkey_access(struct task_struct *tsk, int pkey, @@ -15,7 +17,7 @@ extern int __execute_only_pkey(struct mm_struct *mm); static inline int execute_only_pkey(struct mm_struct *mm) { if (!boot_cpu_has(X86_FEATURE_OSPKE)) - return 0; + return ARCH_DEFAULT_PKEY; return __execute_only_pkey(mm); } @@ -49,13 +51,21 @@ bool mm_pkey_is_allocated(struct mm_struct *mm, int pkey) { /* * "Allocated" pkeys are those that have been returned - * from pkey_alloc(). pkey 0 is special, and never - * returned from pkey_alloc(). + * from pkey_alloc() or pkey 0 which is allocated + * implicitly when the mm is created. */ - if (pkey <= 0) + if (pkey < 0) return false; if (pkey >= arch_max_pkey()) return false; + /* + * The exec-only pkey is set in the allocation map, but + * is not available to any of the user interfaces like + * mprotect_pkey(). + */ + if (pkey == mm->context.execute_only_pkey) + return false; + return mm_pkey_allocation_map(mm) & (1U << pkey); } diff --git a/arch/x86/include/asm/spec-ctrl.h b/arch/x86/include/asm/spec-ctrl.h new file mode 100644 index 00000000000000..ae7c2c5cd7f0e2 --- /dev/null +++ b/arch/x86/include/asm/spec-ctrl.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_SPECCTRL_H_ +#define _ASM_X86_SPECCTRL_H_ + +#include +#include + +/* + * On VMENTER we must preserve whatever view of the SPEC_CTRL MSR + * the guest has, while on VMEXIT we restore the host view. This + * would be easier if SPEC_CTRL were architecturally maskable or + * shadowable for guests but this is not (currently) the case. + * Takes the guest view of SPEC_CTRL MSR as a parameter and also + * the guest's version of VIRT_SPEC_CTRL, if emulated. + */ +extern void x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool guest); + +/** + * x86_spec_ctrl_set_guest - Set speculation control registers for the guest + * @guest_spec_ctrl: The guest content of MSR_SPEC_CTRL + * @guest_virt_spec_ctrl: The guest controlled bits of MSR_VIRT_SPEC_CTRL + * (may get translated to MSR_AMD64_LS_CFG bits) + * + * Avoids writing to the MSR if the content/bits are the same + */ +static inline +void x86_spec_ctrl_set_guest(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl) +{ + x86_virt_spec_ctrl(guest_spec_ctrl, guest_virt_spec_ctrl, true); +} + +/** + * x86_spec_ctrl_restore_host - Restore host speculation control registers + * @guest_spec_ctrl: The guest content of MSR_SPEC_CTRL + * @guest_virt_spec_ctrl: The guest controlled bits of MSR_VIRT_SPEC_CTRL + * (may get translated to MSR_AMD64_LS_CFG bits) + * + * Avoids writing to the MSR if the content/bits are the same + */ +static inline +void x86_spec_ctrl_restore_host(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl) +{ + x86_virt_spec_ctrl(guest_spec_ctrl, guest_virt_spec_ctrl, false); +} + +/* AMD specific Speculative Store Bypass MSR data */ +extern u64 x86_amd_ls_cfg_base; +extern u64 x86_amd_ls_cfg_ssbd_mask; + +static inline u64 ssbd_tif_to_spec_ctrl(u64 tifn) +{ + BUILD_BUG_ON(TIF_SSBD < SPEC_CTRL_SSBD_SHIFT); + return (tifn & _TIF_SSBD) >> (TIF_SSBD - SPEC_CTRL_SSBD_SHIFT); +} + +static inline unsigned long ssbd_spec_ctrl_to_tif(u64 spec_ctrl) +{ + BUILD_BUG_ON(TIF_SSBD < SPEC_CTRL_SSBD_SHIFT); + return (spec_ctrl & SPEC_CTRL_SSBD) << (TIF_SSBD - SPEC_CTRL_SSBD_SHIFT); +} + +static inline u64 ssbd_tif_to_amd_ls_cfg(u64 tifn) +{ + return (tifn & _TIF_SSBD) ? x86_amd_ls_cfg_ssbd_mask : 0ULL; +} + +#ifdef CONFIG_SMP +extern void speculative_store_bypass_ht_init(void); +#else +static inline void speculative_store_bypass_ht_init(void) { } +#endif + +extern void speculative_store_bypass_update(unsigned long tif); + +static inline void speculative_store_bypass_update_current(void) +{ + speculative_store_bypass_update(current_thread_info()->flags); +} + +#endif diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index a5d9521bb2cbaa..2ff2a30a264f4c 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -79,6 +79,7 @@ struct thread_info { #define TIF_SIGPENDING 2 /* signal pending */ #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ #define TIF_SINGLESTEP 4 /* reenable singlestep on user return*/ +#define TIF_SSBD 5 /* Reduced data speculation */ #define TIF_SYSCALL_EMU 6 /* syscall emulation active */ #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ #define TIF_SECCOMP 8 /* secure computing */ @@ -105,6 +106,7 @@ struct thread_info { #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) +#define _TIF_SSBD (1 << TIF_SSBD) #define _TIF_SYSCALL_EMU (1 << TIF_SYSCALL_EMU) #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) #define _TIF_SECCOMP (1 << TIF_SECCOMP) @@ -144,7 +146,7 @@ struct thread_info { /* flags to check in __switch_to() */ #define _TIF_WORK_CTXSW \ - (_TIF_IO_BITMAP|_TIF_NOCPUID|_TIF_NOTSC|_TIF_BLOCKSTEP) + (_TIF_IO_BITMAP|_TIF_NOCPUID|_TIF_NOTSC|_TIF_BLOCKSTEP|_TIF_SSBD) #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW) diff --git a/arch/x86/include/asm/trace/irq_vectors.h b/arch/x86/include/asm/trace/irq_vectors.h index 22647a642e98c4..0af81b590a0c44 100644 --- a/arch/x86/include/asm/trace/irq_vectors.h +++ b/arch/x86/include/asm/trace/irq_vectors.h @@ -236,7 +236,7 @@ TRACE_EVENT(vector_alloc, TP_PROTO(unsigned int irq, unsigned int vector, bool reserved, int ret), - TP_ARGS(irq, vector, ret, reserved), + TP_ARGS(irq, vector, reserved, ret), TP_STRUCT__entry( __field( unsigned int, irq ) diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h index a06cbf01974462..cdaf80de0174c9 100644 --- a/arch/x86/include/uapi/asm/bootparam.h +++ b/arch/x86/include/uapi/asm/bootparam.h @@ -239,7 +239,7 @@ enum x86_hardware_subarch { X86_SUBARCH_PC = 0, X86_SUBARCH_LGUEST, X86_SUBARCH_XEN, - X86_SUBARCH_INTEL_MID, + X86_SUBARCH_INTEL_MID, /* intel手持设备*/ X86_SUBARCH_CE4100, X86_NR_SUBARCHS, }; diff --git a/arch/x86/include/uapi/asm/kvm_para.h b/arch/x86/include/uapi/asm/kvm_para.h index 4c851ebb3cebd4..0ede697c396119 100644 --- a/arch/x86/include/uapi/asm/kvm_para.h +++ b/arch/x86/include/uapi/asm/kvm_para.h @@ -29,7 +29,7 @@ #define KVM_FEATURE_PV_TLB_FLUSH 9 #define KVM_FEATURE_ASYNC_PF_VMEXIT 10 -#define KVM_HINTS_DEDICATED 0 +#define KVM_HINTS_REALTIME 0 /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index c88e0b127810f2..b481b95bd8f6b9 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -14,8 +14,11 @@ #include #define PCI_DEVICE_ID_AMD_17H_ROOT 0x1450 +#define PCI_DEVICE_ID_AMD_17H_M10H_ROOT 0x15d0 #define PCI_DEVICE_ID_AMD_17H_DF_F3 0x1463 #define PCI_DEVICE_ID_AMD_17H_DF_F4 0x1464 +#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F3 0x15eb +#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F4 0x15ec /* Protect the PCI config register pairs used for SMN and DF indirect access. */ static DEFINE_MUTEX(smn_mutex); @@ -24,6 +27,7 @@ static u32 *flush_words; static const struct pci_device_id amd_root_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_ROOT) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_ROOT) }, {} }; @@ -39,6 +43,7 @@ const struct pci_device_id amd_nb_misc_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, {} }; @@ -51,6 +56,7 @@ static const struct pci_device_id amd_nb_link_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F4) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F4) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F4) }, {} }; diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index 2aabd4cb0e3f1d..f1c2fb94a995bb 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -527,12 +527,12 @@ static struct clock_event_device lapic_clockevent = { CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP | CLOCK_EVT_FEAT_DUMMY, .shift = 32, - .set_state_shutdown = lapic_timer_shutdown, + .set_state_shutdown = lapic_timer_shutdown, /* 屏蔽 all dev's IRQ */ .set_state_periodic = lapic_timer_set_periodic, .set_state_oneshot = lapic_timer_set_oneshot, .set_state_oneshot_stopped = lapic_timer_shutdown, - .set_next_event = lapic_next_event, - .broadcast = lapic_timer_broadcast, + .set_next_event = lapic_next_event, /* oneshot模式timer中断下次到来的时间间隔 */ + .broadcast = lapic_timer_broadcast, /* 广播"本CPU中断"到其它CPUs */ .rating = 100, .irq = -1, }; diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 7553819c74c35f..3982f79d2377c6 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -1851,7 +1851,7 @@ static void ioapic_ir_ack_level(struct irq_data *irq_data) * intr-remapping table entry. Hence for the io-apic * EOI we use the pin number. */ - ack_APIC_irq(); + apic_ack_irq(irq_data); eoi_ioapic_pin(data->entry.vector, data); } diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c index bb6f7a2148d778..b708f597eee3ab 100644 --- a/arch/x86/kernel/apic/vector.c +++ b/arch/x86/kernel/apic/vector.c @@ -235,6 +235,15 @@ static int allocate_vector(struct irq_data *irqd, const struct cpumask *dest) if (vector && cpu_online(cpu) && cpumask_test_cpu(cpu, dest)) return 0; + /* + * Careful here. @apicd might either have move_in_progress set or + * be enqueued for cleanup. Assigning a new vector would either + * leave a stale vector on some CPU around or in case of a pending + * cleanup corrupt the hlist. + */ + if (apicd->move_in_progress || !hlist_unhashed(&apicd->clist)) + return -EBUSY; + vector = irq_matrix_alloc(vector_matrix, dest, resvd, &cpu); if (vector > 0) apic_update_vector(irqd, vector, cpu); @@ -800,13 +809,18 @@ static int apic_retrigger_irq(struct irq_data *irqd) return 1; } -void apic_ack_edge(struct irq_data *irqd) +void apic_ack_irq(struct irq_data *irqd) { - irq_complete_move(irqd_cfg(irqd)); irq_move_irq(irqd); ack_APIC_irq(); } +void apic_ack_edge(struct irq_data *irqd) +{ + irq_complete_move(irqd_cfg(irqd)); + apic_ack_irq(irqd); +} + static struct irq_chip lapic_controller = { .name = "APIC", .irq_ack = apic_ack_edge, diff --git a/arch/x86/kernel/apic/x2apic_cluster.c b/arch/x86/kernel/apic/x2apic_cluster.c index 8b04234e010b26..7685444a106bb2 100644 --- a/arch/x86/kernel/apic/x2apic_cluster.c +++ b/arch/x86/kernel/apic/x2apic_cluster.c @@ -116,6 +116,7 @@ static void init_x2apic_ldr(void) goto update; } cmsk = cluster_hotplug_mask; + cmsk->clusterid = cluster; cluster_hotplug_mask = NULL; update: this_cpu_write(cluster_masks, cmsk); diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 12bc0a1139dac5..1b18be3f35a8e8 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -554,6 +555,26 @@ static void bsp_init_amd(struct cpuinfo_x86 *c) rdmsrl(MSR_FAM10H_NODE_ID, value); nodes_per_socket = ((value >> 3) & 7) + 1; } + + if (c->x86 >= 0x15 && c->x86 <= 0x17) { + unsigned int bit; + + switch (c->x86) { + case 0x15: bit = 54; break; + case 0x16: bit = 33; break; + case 0x17: bit = 10; break; + default: return; + } + /* + * Try to cache the base value so further operations can + * avoid RMW. If that faults, do not enable SSBD. + */ + if (!rdmsrl_safe(MSR_AMD64_LS_CFG, &x86_amd_ls_cfg_base)) { + setup_force_cpu_cap(X86_FEATURE_LS_CFG_SSBD); + setup_force_cpu_cap(X86_FEATURE_SSBD); + x86_amd_ls_cfg_ssbd_mask = 1ULL << bit; + } + } } static void early_detect_mem_encrypt(struct cpuinfo_x86 *c) @@ -791,6 +812,7 @@ static void init_amd_bd(struct cpuinfo_x86 *c) static void init_amd_zn(struct cpuinfo_x86 *c) { + set_cpu_cap(c, X86_FEATURE_ZEN); /* * Fix erratum 1076: CPB feature bit not being set in CPUID. It affects * all up to and including B1. diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index bfca937bdcc36c..7416fc206b4a0e 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -12,8 +12,10 @@ #include #include #include +#include +#include -#include +#include #include #include #include @@ -27,6 +29,27 @@ #include static void __init spectre_v2_select_mitigation(void); +static void __init ssb_select_mitigation(void); + +/* + * Our boot-time value of the SPEC_CTRL MSR. We read it once so that any + * writes to SPEC_CTRL contain whatever reserved bits have been set. + */ +u64 __ro_after_init x86_spec_ctrl_base; +EXPORT_SYMBOL_GPL(x86_spec_ctrl_base); + +/* + * The vendor and possibly platform specific bits which can be modified in + * x86_spec_ctrl_base. + */ +static u64 __ro_after_init x86_spec_ctrl_mask = SPEC_CTRL_IBRS; + +/* + * AMD specific MSR info for Speculative Store Bypass control. + * x86_amd_ls_cfg_ssbd_mask is initialized in identify_boot_cpu(). + */ +u64 __ro_after_init x86_amd_ls_cfg_base; +u64 __ro_after_init x86_amd_ls_cfg_ssbd_mask; void __init check_bugs(void) { @@ -37,9 +60,27 @@ void __init check_bugs(void) print_cpu_info(&boot_cpu_data); } + /* + * Read the SPEC_CTRL MSR to account for reserved bits which may + * have unknown values. AMD64_LS_CFG MSR is cached in the early AMD + * init code as it is not enumerated and depends on the family. + */ + if (boot_cpu_has(X86_FEATURE_MSR_SPEC_CTRL)) + rdmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base); + + /* Allow STIBP in MSR_SPEC_CTRL if supported */ + if (boot_cpu_has(X86_FEATURE_STIBP)) + x86_spec_ctrl_mask |= SPEC_CTRL_STIBP; + /* Select the proper spectre mitigation before patching alternatives */ spectre_v2_select_mitigation(); + /* + * Select proper mitigation for any exposure to the Speculative Store + * Bypass vulnerability. + */ + ssb_select_mitigation(); + #ifdef CONFIG_X86_32 /* * Check whether we are able to run this kernel safely on SMP. @@ -93,7 +134,76 @@ static const char *spectre_v2_strings[] = { #undef pr_fmt #define pr_fmt(fmt) "Spectre V2 : " fmt -static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE; +static enum spectre_v2_mitigation spectre_v2_enabled __ro_after_init = + SPECTRE_V2_NONE; + +void +x86_virt_spec_ctrl(u64 guest_spec_ctrl, u64 guest_virt_spec_ctrl, bool setguest) +{ + u64 msrval, guestval, hostval = x86_spec_ctrl_base; + struct thread_info *ti = current_thread_info(); + + /* Is MSR_SPEC_CTRL implemented ? */ + if (static_cpu_has(X86_FEATURE_MSR_SPEC_CTRL)) { + /* + * Restrict guest_spec_ctrl to supported values. Clear the + * modifiable bits in the host base value and or the + * modifiable bits from the guest value. + */ + guestval = hostval & ~x86_spec_ctrl_mask; + guestval |= guest_spec_ctrl & x86_spec_ctrl_mask; + + /* SSBD controlled in MSR_SPEC_CTRL */ + if (static_cpu_has(X86_FEATURE_SPEC_CTRL_SSBD)) + hostval |= ssbd_tif_to_spec_ctrl(ti->flags); + + if (hostval != guestval) { + msrval = setguest ? guestval : hostval; + wrmsrl(MSR_IA32_SPEC_CTRL, msrval); + } + } + + /* + * If SSBD is not handled in MSR_SPEC_CTRL on AMD, update + * MSR_AMD64_L2_CFG or MSR_VIRT_SPEC_CTRL if supported. + */ + if (!static_cpu_has(X86_FEATURE_LS_CFG_SSBD) && + !static_cpu_has(X86_FEATURE_VIRT_SSBD)) + return; + + /* + * If the host has SSBD mitigation enabled, force it in the host's + * virtual MSR value. If its not permanently enabled, evaluate + * current's TIF_SSBD thread flag. + */ + if (static_cpu_has(X86_FEATURE_SPEC_STORE_BYPASS_DISABLE)) + hostval = SPEC_CTRL_SSBD; + else + hostval = ssbd_tif_to_spec_ctrl(ti->flags); + + /* Sanitize the guest value */ + guestval = guest_virt_spec_ctrl & SPEC_CTRL_SSBD; + + if (hostval != guestval) { + unsigned long tif; + + tif = setguest ? ssbd_spec_ctrl_to_tif(guestval) : + ssbd_spec_ctrl_to_tif(hostval); + + speculative_store_bypass_update(tif); + } +} +EXPORT_SYMBOL_GPL(x86_virt_spec_ctrl); + +static void x86_amd_ssb_disable(void) +{ + u64 msrval = x86_amd_ls_cfg_base | x86_amd_ls_cfg_ssbd_mask; + + if (boot_cpu_has(X86_FEATURE_VIRT_SSBD)) + wrmsrl(MSR_AMD64_VIRT_SPEC_CTRL, SPEC_CTRL_SSBD); + else if (boot_cpu_has(X86_FEATURE_LS_CFG_SSBD)) + wrmsrl(MSR_AMD64_LS_CFG, msrval); +} #ifdef RETPOLINE static bool spectre_v2_bad_module; @@ -312,32 +422,289 @@ static void __init spectre_v2_select_mitigation(void) } #undef pr_fmt +#define pr_fmt(fmt) "Speculative Store Bypass: " fmt + +static enum ssb_mitigation ssb_mode __ro_after_init = SPEC_STORE_BYPASS_NONE; + +/* The kernel command line selection */ +enum ssb_mitigation_cmd { + SPEC_STORE_BYPASS_CMD_NONE, + SPEC_STORE_BYPASS_CMD_AUTO, + SPEC_STORE_BYPASS_CMD_ON, + SPEC_STORE_BYPASS_CMD_PRCTL, + SPEC_STORE_BYPASS_CMD_SECCOMP, +}; + +static const char *ssb_strings[] = { + [SPEC_STORE_BYPASS_NONE] = "Vulnerable", + [SPEC_STORE_BYPASS_DISABLE] = "Mitigation: Speculative Store Bypass disabled", + [SPEC_STORE_BYPASS_PRCTL] = "Mitigation: Speculative Store Bypass disabled via prctl", + [SPEC_STORE_BYPASS_SECCOMP] = "Mitigation: Speculative Store Bypass disabled via prctl and seccomp", +}; + +static const struct { + const char *option; + enum ssb_mitigation_cmd cmd; +} ssb_mitigation_options[] = { + { "auto", SPEC_STORE_BYPASS_CMD_AUTO }, /* Platform decides */ + { "on", SPEC_STORE_BYPASS_CMD_ON }, /* Disable Speculative Store Bypass */ + { "off", SPEC_STORE_BYPASS_CMD_NONE }, /* Don't touch Speculative Store Bypass */ + { "prctl", SPEC_STORE_BYPASS_CMD_PRCTL }, /* Disable Speculative Store Bypass via prctl */ + { "seccomp", SPEC_STORE_BYPASS_CMD_SECCOMP }, /* Disable Speculative Store Bypass via prctl and seccomp */ +}; + +static enum ssb_mitigation_cmd __init ssb_parse_cmdline(void) +{ + enum ssb_mitigation_cmd cmd = SPEC_STORE_BYPASS_CMD_AUTO; + char arg[20]; + int ret, i; + + if (cmdline_find_option_bool(boot_command_line, "nospec_store_bypass_disable")) { + return SPEC_STORE_BYPASS_CMD_NONE; + } else { + ret = cmdline_find_option(boot_command_line, "spec_store_bypass_disable", + arg, sizeof(arg)); + if (ret < 0) + return SPEC_STORE_BYPASS_CMD_AUTO; + + for (i = 0; i < ARRAY_SIZE(ssb_mitigation_options); i++) { + if (!match_option(arg, ret, ssb_mitigation_options[i].option)) + continue; + + cmd = ssb_mitigation_options[i].cmd; + break; + } + + if (i >= ARRAY_SIZE(ssb_mitigation_options)) { + pr_err("unknown option (%s). Switching to AUTO select\n", arg); + return SPEC_STORE_BYPASS_CMD_AUTO; + } + } + + return cmd; +} + +static enum ssb_mitigation __init __ssb_select_mitigation(void) +{ + enum ssb_mitigation mode = SPEC_STORE_BYPASS_NONE; + enum ssb_mitigation_cmd cmd; + + if (!boot_cpu_has(X86_FEATURE_SSBD)) + return mode; + + cmd = ssb_parse_cmdline(); + if (!boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS) && + (cmd == SPEC_STORE_BYPASS_CMD_NONE || + cmd == SPEC_STORE_BYPASS_CMD_AUTO)) + return mode; + + switch (cmd) { + case SPEC_STORE_BYPASS_CMD_AUTO: + case SPEC_STORE_BYPASS_CMD_SECCOMP: + /* + * Choose prctl+seccomp as the default mode if seccomp is + * enabled. + */ + if (IS_ENABLED(CONFIG_SECCOMP)) + mode = SPEC_STORE_BYPASS_SECCOMP; + else + mode = SPEC_STORE_BYPASS_PRCTL; + break; + case SPEC_STORE_BYPASS_CMD_ON: + mode = SPEC_STORE_BYPASS_DISABLE; + break; + case SPEC_STORE_BYPASS_CMD_PRCTL: + mode = SPEC_STORE_BYPASS_PRCTL; + break; + case SPEC_STORE_BYPASS_CMD_NONE: + break; + } + + /* + * We have three CPU feature flags that are in play here: + * - X86_BUG_SPEC_STORE_BYPASS - CPU is susceptible. + * - X86_FEATURE_SSBD - CPU is able to turn off speculative store bypass + * - X86_FEATURE_SPEC_STORE_BYPASS_DISABLE - engage the mitigation + */ + if (mode == SPEC_STORE_BYPASS_DISABLE) { + setup_force_cpu_cap(X86_FEATURE_SPEC_STORE_BYPASS_DISABLE); + /* + * Intel uses the SPEC CTRL MSR Bit(2) for this, while AMD uses + * a completely different MSR and bit dependent on family. + */ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_INTEL: + x86_spec_ctrl_base |= SPEC_CTRL_SSBD; + x86_spec_ctrl_mask |= SPEC_CTRL_SSBD; + wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base); + break; + case X86_VENDOR_AMD: + x86_amd_ssb_disable(); + break; + } + } + + return mode; +} + +static void ssb_select_mitigation(void) +{ + ssb_mode = __ssb_select_mitigation(); + + if (boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS)) + pr_info("%s\n", ssb_strings[ssb_mode]); +} + +#undef pr_fmt +#define pr_fmt(fmt) "Speculation prctl: " fmt + +static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl) +{ + bool update; + + if (ssb_mode != SPEC_STORE_BYPASS_PRCTL && + ssb_mode != SPEC_STORE_BYPASS_SECCOMP) + return -ENXIO; + + switch (ctrl) { + case PR_SPEC_ENABLE: + /* If speculation is force disabled, enable is not allowed */ + if (task_spec_ssb_force_disable(task)) + return -EPERM; + task_clear_spec_ssb_disable(task); + update = test_and_clear_tsk_thread_flag(task, TIF_SSBD); + break; + case PR_SPEC_DISABLE: + task_set_spec_ssb_disable(task); + update = !test_and_set_tsk_thread_flag(task, TIF_SSBD); + break; + case PR_SPEC_FORCE_DISABLE: + task_set_spec_ssb_disable(task); + task_set_spec_ssb_force_disable(task); + update = !test_and_set_tsk_thread_flag(task, TIF_SSBD); + break; + default: + return -ERANGE; + } + + /* + * If being set on non-current task, delay setting the CPU + * mitigation until it is next scheduled. + */ + if (task == current && update) + speculative_store_bypass_update_current(); + + return 0; +} + +int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which, + unsigned long ctrl) +{ + switch (which) { + case PR_SPEC_STORE_BYPASS: + return ssb_prctl_set(task, ctrl); + default: + return -ENODEV; + } +} + +#ifdef CONFIG_SECCOMP +void arch_seccomp_spec_mitigate(struct task_struct *task) +{ + if (ssb_mode == SPEC_STORE_BYPASS_SECCOMP) + ssb_prctl_set(task, PR_SPEC_FORCE_DISABLE); +} +#endif + +static int ssb_prctl_get(struct task_struct *task) +{ + switch (ssb_mode) { + case SPEC_STORE_BYPASS_DISABLE: + return PR_SPEC_DISABLE; + case SPEC_STORE_BYPASS_SECCOMP: + case SPEC_STORE_BYPASS_PRCTL: + if (task_spec_ssb_force_disable(task)) + return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE; + if (task_spec_ssb_disable(task)) + return PR_SPEC_PRCTL | PR_SPEC_DISABLE; + return PR_SPEC_PRCTL | PR_SPEC_ENABLE; + default: + if (boot_cpu_has_bug(X86_BUG_SPEC_STORE_BYPASS)) + return PR_SPEC_ENABLE; + return PR_SPEC_NOT_AFFECTED; + } +} + +int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) +{ + switch (which) { + case PR_SPEC_STORE_BYPASS: + return ssb_prctl_get(task); + default: + return -ENODEV; + } +} + +void x86_spec_ctrl_setup_ap(void) +{ + if (boot_cpu_has(X86_FEATURE_MSR_SPEC_CTRL)) + wrmsrl(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base); + + if (ssb_mode == SPEC_STORE_BYPASS_DISABLE) + x86_amd_ssb_disable(); +} #ifdef CONFIG_SYSFS -ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf) + +static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr, + char *buf, unsigned int bug) { - if (!boot_cpu_has_bug(X86_BUG_CPU_MELTDOWN)) + if (!boot_cpu_has_bug(bug)) return sprintf(buf, "Not affected\n"); - if (boot_cpu_has(X86_FEATURE_PTI)) - return sprintf(buf, "Mitigation: PTI\n"); + + switch (bug) { + case X86_BUG_CPU_MELTDOWN: + if (boot_cpu_has(X86_FEATURE_PTI)) + return sprintf(buf, "Mitigation: PTI\n"); + + break; + + case X86_BUG_SPECTRE_V1: + return sprintf(buf, "Mitigation: __user pointer sanitization\n"); + + case X86_BUG_SPECTRE_V2: + return sprintf(buf, "%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled], + boot_cpu_has(X86_FEATURE_USE_IBPB) ? ", IBPB" : "", + boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "", + spectre_v2_module_string()); + + case X86_BUG_SPEC_STORE_BYPASS: + return sprintf(buf, "%s\n", ssb_strings[ssb_mode]); + + default: + break; + } + return sprintf(buf, "Vulnerable\n"); } +ssize_t cpu_show_meltdown(struct device *dev, struct device_attribute *attr, char *buf) +{ + return cpu_show_common(dev, attr, buf, X86_BUG_CPU_MELTDOWN); +} + ssize_t cpu_show_spectre_v1(struct device *dev, struct device_attribute *attr, char *buf) { - if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V1)) - return sprintf(buf, "Not affected\n"); - return sprintf(buf, "Mitigation: __user pointer sanitization\n"); + return cpu_show_common(dev, attr, buf, X86_BUG_SPECTRE_V1); } ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, char *buf) { - if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2)) - return sprintf(buf, "Not affected\n"); + return cpu_show_common(dev, attr, buf, X86_BUG_SPECTRE_V2); +} - return sprintf(buf, "%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled], - boot_cpu_has(X86_FEATURE_USE_IBPB) ? ", IBPB" : "", - boot_cpu_has(X86_FEATURE_USE_IBRS_FW) ? ", IBRS_FW" : "", - spectre_v2_module_string()); +ssize_t cpu_show_spec_store_bypass(struct device *dev, struct device_attribute *attr, char *buf) +{ + return cpu_show_common(dev, attr, buf, X86_BUG_SPEC_STORE_BYPASS); } #endif diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index ce243f7d2d4e08..38276f58d3bfe2 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -757,17 +757,32 @@ static void init_speculation_control(struct cpuinfo_x86 *c) * and they also have a different bit for STIBP support. Also, * a hypervisor might have set the individual AMD bits even on * Intel CPUs, for finer-grained selection of what's available. - * - * We use the AMD bits in 0x8000_0008 EBX as the generic hardware - * features, which are visible in /proc/cpuinfo and used by the - * kernel. So set those accordingly from the Intel bits. */ if (cpu_has(c, X86_FEATURE_SPEC_CTRL)) { set_cpu_cap(c, X86_FEATURE_IBRS); set_cpu_cap(c, X86_FEATURE_IBPB); + set_cpu_cap(c, X86_FEATURE_MSR_SPEC_CTRL); } + if (cpu_has(c, X86_FEATURE_INTEL_STIBP)) set_cpu_cap(c, X86_FEATURE_STIBP); + + if (cpu_has(c, X86_FEATURE_SPEC_CTRL_SSBD) || + cpu_has(c, X86_FEATURE_VIRT_SSBD)) + set_cpu_cap(c, X86_FEATURE_SSBD); + + if (cpu_has(c, X86_FEATURE_AMD_IBRS)) { + set_cpu_cap(c, X86_FEATURE_IBRS); + set_cpu_cap(c, X86_FEATURE_MSR_SPEC_CTRL); + } + + if (cpu_has(c, X86_FEATURE_AMD_IBPB)) + set_cpu_cap(c, X86_FEATURE_IBPB); + + if (cpu_has(c, X86_FEATURE_AMD_STIBP)) { + set_cpu_cap(c, X86_FEATURE_STIBP); + set_cpu_cap(c, X86_FEATURE_MSR_SPEC_CTRL); + } } void get_cpu_cap(struct cpuinfo_x86 *c) @@ -927,21 +942,47 @@ static const __initconst struct x86_cpu_id cpu_no_meltdown[] = { {} }; -static bool __init cpu_vulnerable_to_meltdown(struct cpuinfo_x86 *c) +/* Only list CPUs which speculate but are non susceptible to SSB */ +static const __initconst struct x86_cpu_id cpu_no_spec_store_bypass[] = { + { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT1 }, + { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_AIRMONT }, + { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_SILVERMONT2 }, + { X86_VENDOR_INTEL, 6, INTEL_FAM6_ATOM_MERRIFIELD }, + { X86_VENDOR_INTEL, 6, INTEL_FAM6_CORE_YONAH }, + { X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNL }, + { X86_VENDOR_INTEL, 6, INTEL_FAM6_XEON_PHI_KNM }, + { X86_VENDOR_AMD, 0x12, }, + { X86_VENDOR_AMD, 0x11, }, + { X86_VENDOR_AMD, 0x10, }, + { X86_VENDOR_AMD, 0xf, }, + {} +}; + +static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c) { u64 ia32_cap = 0; - if (x86_match_cpu(cpu_no_meltdown)) - return false; + if (x86_match_cpu(cpu_no_speculation)) + return; + + setup_force_cpu_bug(X86_BUG_SPECTRE_V1); + setup_force_cpu_bug(X86_BUG_SPECTRE_V2); if (cpu_has(c, X86_FEATURE_ARCH_CAPABILITIES)) rdmsrl(MSR_IA32_ARCH_CAPABILITIES, ia32_cap); + if (!x86_match_cpu(cpu_no_spec_store_bypass) && + !(ia32_cap & ARCH_CAP_SSB_NO)) + setup_force_cpu_bug(X86_BUG_SPEC_STORE_BYPASS); + + if (x86_match_cpu(cpu_no_meltdown)) + return; + /* Rogue Data Cache Load? No! */ if (ia32_cap & ARCH_CAP_RDCL_NO) - return false; + return; - return true; + setup_force_cpu_bug(X86_BUG_CPU_MELTDOWN); } /* @@ -992,12 +1033,7 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c) setup_force_cpu_cap(X86_FEATURE_ALWAYS); - if (!x86_match_cpu(cpu_no_speculation)) { - if (cpu_vulnerable_to_meltdown(c)) - setup_force_cpu_bug(X86_BUG_CPU_MELTDOWN); - setup_force_cpu_bug(X86_BUG_SPECTRE_V1); - setup_force_cpu_bug(X86_BUG_SPECTRE_V2); - } + cpu_set_bug_bits(c); fpu__init_system(c); @@ -1359,6 +1395,7 @@ void identify_secondary_cpu(struct cpuinfo_x86 *c) #endif mtrr_ap_init(); validate_apic_and_package_id(c); + x86_spec_ctrl_setup_ap(); } static __init int setup_noclflush(char *arg) diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h index e806b11a99af4c..37672d299e3574 100644 --- a/arch/x86/kernel/cpu/cpu.h +++ b/arch/x86/kernel/cpu/cpu.h @@ -50,4 +50,6 @@ extern void cpu_detect_cache_sizes(struct cpuinfo_x86 *c); unsigned int aperfmperf_get_khz(int cpu); +extern void x86_spec_ctrl_setup_ap(void); + #endif /* ARCH_X86_CPU_H */ diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index 60d1897041da89..577e7f7ae2733f 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -188,7 +188,10 @@ static void early_init_intel(struct cpuinfo_x86 *c) setup_clear_cpu_cap(X86_FEATURE_IBPB); setup_clear_cpu_cap(X86_FEATURE_STIBP); setup_clear_cpu_cap(X86_FEATURE_SPEC_CTRL); + setup_clear_cpu_cap(X86_FEATURE_MSR_SPEC_CTRL); setup_clear_cpu_cap(X86_FEATURE_INTEL_STIBP); + setup_clear_cpu_cap(X86_FEATURE_SSBD); + setup_clear_cpu_cap(X86_FEATURE_SPEC_CTRL_SSBD); } /* diff --git a/arch/x86/kernel/cpu/intel_rdt.c b/arch/x86/kernel/cpu/intel_rdt.c index 589b948e6e01f0..316a8875bd9061 100644 --- a/arch/x86/kernel/cpu/intel_rdt.c +++ b/arch/x86/kernel/cpu/intel_rdt.c @@ -821,6 +821,8 @@ static __init void rdt_quirks(void) case INTEL_FAM6_SKYLAKE_X: if (boot_cpu_data.x86_stepping <= 4) set_rdt_options("!cmt,!mbmtotal,!mbmlocal,!l3cat"); + else + set_rdt_options("!l3cat"); } } diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c index 475cb4f5f14fac..c805a06e14c388 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-inject.c +++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c @@ -48,7 +48,7 @@ static struct dentry *dfs_inj; static u8 n_banks; -#define MAX_FLAG_OPT_SIZE 3 +#define MAX_FLAG_OPT_SIZE 4 #define NBCFG 0x44 enum injection_type { diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c index f7666eef4a879b..c8e03880059167 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c @@ -94,6 +94,11 @@ static struct smca_bank_name smca_names[] = { [SMCA_SMU] = { "smu", "System Management Unit" }, }; +static u32 smca_bank_addrs[MAX_NR_BANKS][NR_BLOCKS] __ro_after_init = +{ + [0 ... MAX_NR_BANKS - 1] = { [0 ... NR_BLOCKS - 1] = -1 } +}; + const char *smca_get_name(enum smca_bank_types t) { if (t >= N_SMCA_BANK_TYPES) @@ -443,20 +448,26 @@ static u32 smca_get_block_address(unsigned int cpu, unsigned int bank, if (!block) return MSR_AMD64_SMCA_MCx_MISC(bank); + /* Check our cache first: */ + if (smca_bank_addrs[bank][block] != -1) + return smca_bank_addrs[bank][block]; + /* * For SMCA enabled processors, BLKPTR field of the first MISC register * (MCx_MISC0) indicates presence of additional MISC regs set (MISC1-4). */ if (rdmsr_safe_on_cpu(cpu, MSR_AMD64_SMCA_MCx_CONFIG(bank), &low, &high)) - return addr; + goto out; if (!(low & MCI_CONFIG_MCAX)) - return addr; + goto out; if (!rdmsr_safe_on_cpu(cpu, MSR_AMD64_SMCA_MCx_MISC(bank), &low, &high) && (low & MASK_BLKPTR_LO)) - return MSR_AMD64_SMCA_MCx_MISCy(bank, block - 1); + addr = MSR_AMD64_SMCA_MCx_MISCy(bank, block - 1); +out: + smca_bank_addrs[bank][block] = addr; return addr; } @@ -468,18 +479,6 @@ static u32 get_block_address(unsigned int cpu, u32 current_addr, u32 low, u32 hi if ((bank >= mca_cfg.banks) || (block >= NR_BLOCKS)) return addr; - /* Get address from already initialized block. */ - if (per_cpu(threshold_banks, cpu)) { - struct threshold_bank *bankp = per_cpu(threshold_banks, cpu)[bank]; - - if (bankp && bankp->blocks) { - struct threshold_block *blockp = &bankp->blocks[block]; - - if (blockp) - return blockp->address; - } - } - if (mce_flags.smca) return smca_get_block_address(cpu, bank, block); diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 0c408f8c4ed465..2d29e47c056ea1 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -104,6 +104,12 @@ static bool __head check_la57_support(unsigned long physaddr) } #endif +/* Code in __startup_64() can be relocated during execution, but the compiler + * doesn't have to generate PC-relative relocations when accessing globals from + * that function. Clang actually does not generate them, which leads to + * boot-time crashes. To work around this problem, every global pointer must + * be adjusted using fixup_pointer(). + */ unsigned long __head __startup_64(unsigned long physaddr, struct boot_params *bp) { @@ -113,6 +119,7 @@ unsigned long __head __startup_64(unsigned long physaddr, p4dval_t *p4d; pudval_t *pud; pmdval_t *pmd, pmd_entry; + pteval_t *mask_ptr; bool la57; int i; unsigned int *next_pgt_ptr; @@ -196,7 +203,8 @@ unsigned long __head __startup_64(unsigned long physaddr, pmd_entry = __PAGE_KERNEL_LARGE_EXEC & ~_PAGE_GLOBAL; /* Filter out unsupported __PAGE_KERNEL_* bits: */ - pmd_entry &= __supported_pte_mask; + mask_ptr = fixup_pointer(&__supported_pte_mask, physaddr); + pmd_entry &= *mask_ptr; pmd_entry += sme_get_me_mask(); pmd_entry += physaddr; diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c index 0715f827607c4a..6f4d42377fe520 100644 --- a/arch/x86/kernel/kprobes/core.c +++ b/arch/x86/kernel/kprobes/core.c @@ -370,6 +370,10 @@ int __copy_instruction(u8 *dest, u8 *src, u8 *real, struct insn *insn) if (insn->opcode.bytes[0] == BREAKPOINT_INSTRUCTION) return 0; + /* We should not singlestep on the exception masking instructions */ + if (insn_masking_exception(insn)) + return 0; + #ifdef CONFIG_X86_64 /* Only x86_64 has RIP relative instructions */ if (insn_rip_relative(insn)) { diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index 7867417cfaff2b..5b2300b818af93 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -457,7 +457,7 @@ static void __init sev_map_percpu_data(void) static void __init kvm_smp_prepare_cpus(unsigned int max_cpus) { native_smp_prepare_cpus(max_cpus); - if (kvm_para_has_hint(KVM_HINTS_DEDICATED)) + if (kvm_para_has_hint(KVM_HINTS_REALTIME)) static_branch_disable(&virt_spin_lock_key); } @@ -553,7 +553,7 @@ static void __init kvm_guest_init(void) } if (kvm_para_has_feature(KVM_FEATURE_PV_TLB_FLUSH) && - !kvm_para_has_hint(KVM_HINTS_DEDICATED) && + !kvm_para_has_hint(KVM_HINTS_REALTIME) && kvm_para_has_feature(KVM_FEATURE_STEAL_TIME)) pv_mmu_ops.flush_tlb_others = kvm_flush_tlb_others; @@ -649,7 +649,7 @@ static __init int kvm_setup_pv_tlb_flush(void) int cpu; if (kvm_para_has_feature(KVM_FEATURE_PV_TLB_FLUSH) && - !kvm_para_has_hint(KVM_HINTS_DEDICATED) && + !kvm_para_has_hint(KVM_HINTS_REALTIME) && kvm_para_has_feature(KVM_FEATURE_STEAL_TIME)) { for_each_possible_cpu(cpu) { zalloc_cpumask_var_node(per_cpu_ptr(&__pv_tlb_mask, cpu), @@ -745,7 +745,7 @@ void __init kvm_spinlock_init(void) if (!kvm_para_has_feature(KVM_FEATURE_PV_UNHALT)) return; - if (kvm_para_has_hint(KVM_HINTS_DEDICATED)) + if (kvm_para_has_hint(KVM_HINTS_REALTIME)) return; __pv_init_lock_hash(); diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c index 60cdec6628b0d3..d1ab07ec8c9aca 100644 --- a/arch/x86/kernel/machine_kexec_32.c +++ b/arch/x86/kernel/machine_kexec_32.c @@ -57,12 +57,17 @@ static void load_segments(void) static void machine_kexec_free_page_tables(struct kimage *image) { free_page((unsigned long)image->arch.pgd); + image->arch.pgd = NULL; #ifdef CONFIG_X86_PAE free_page((unsigned long)image->arch.pmd0); + image->arch.pmd0 = NULL; free_page((unsigned long)image->arch.pmd1); + image->arch.pmd1 = NULL; #endif free_page((unsigned long)image->arch.pte0); + image->arch.pte0 = NULL; free_page((unsigned long)image->arch.pte1); + image->arch.pte1 = NULL; } static int machine_kexec_alloc_page_tables(struct kimage *image) @@ -79,7 +84,6 @@ static int machine_kexec_alloc_page_tables(struct kimage *image) !image->arch.pmd0 || !image->arch.pmd1 || #endif !image->arch.pte0 || !image->arch.pte1) { - machine_kexec_free_page_tables(image); return -ENOMEM; } return 0; diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index a5e55d832d0a4d..6010449ca6d295 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -39,9 +39,13 @@ const struct kexec_file_ops * const kexec_file_loaders[] = { static void free_transition_pgtable(struct kimage *image) { free_page((unsigned long)image->arch.p4d); + image->arch.p4d = NULL; free_page((unsigned long)image->arch.pud); + image->arch.pud = NULL; free_page((unsigned long)image->arch.pmd); + image->arch.pmd = NULL; free_page((unsigned long)image->arch.pte); + image->arch.pte = NULL; } static int init_transition_pgtable(struct kimage *image, pgd_t *pgd) @@ -91,7 +95,6 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd) set_pte(pte, pfn_pte(paddr >> PAGE_SHIFT, PAGE_KERNEL_EXEC_NOENC)); return 0; err: - free_transition_pgtable(image); return result; } diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 03408b942adbad..30ca2d1a923197 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -38,6 +38,7 @@ #include #include #include +#include /* * per-CPU TSS segments. Threads are completely 'soft' on Linux, @@ -278,6 +279,148 @@ static inline void switch_to_bitmap(struct tss_struct *tss, } } +#ifdef CONFIG_SMP + +struct ssb_state { + struct ssb_state *shared_state; + raw_spinlock_t lock; + unsigned int disable_state; + unsigned long local_state; +}; + +#define LSTATE_SSB 0 + +static DEFINE_PER_CPU(struct ssb_state, ssb_state); + +void speculative_store_bypass_ht_init(void) +{ + struct ssb_state *st = this_cpu_ptr(&ssb_state); + unsigned int this_cpu = smp_processor_id(); + unsigned int cpu; + + st->local_state = 0; + + /* + * Shared state setup happens once on the first bringup + * of the CPU. It's not destroyed on CPU hotunplug. + */ + if (st->shared_state) + return; + + raw_spin_lock_init(&st->lock); + + /* + * Go over HT siblings and check whether one of them has set up the + * shared state pointer already. + */ + for_each_cpu(cpu, topology_sibling_cpumask(this_cpu)) { + if (cpu == this_cpu) + continue; + + if (!per_cpu(ssb_state, cpu).shared_state) + continue; + + /* Link it to the state of the sibling: */ + st->shared_state = per_cpu(ssb_state, cpu).shared_state; + return; + } + + /* + * First HT sibling to come up on the core. Link shared state of + * the first HT sibling to itself. The siblings on the same core + * which come up later will see the shared state pointer and link + * themself to the state of this CPU. + */ + st->shared_state = st; +} + +/* + * Logic is: First HT sibling enables SSBD for both siblings in the core + * and last sibling to disable it, disables it for the whole core. This how + * MSR_SPEC_CTRL works in "hardware": + * + * CORE_SPEC_CTRL = THREAD0_SPEC_CTRL | THREAD1_SPEC_CTRL + */ +static __always_inline void amd_set_core_ssb_state(unsigned long tifn) +{ + struct ssb_state *st = this_cpu_ptr(&ssb_state); + u64 msr = x86_amd_ls_cfg_base; + + if (!static_cpu_has(X86_FEATURE_ZEN)) { + msr |= ssbd_tif_to_amd_ls_cfg(tifn); + wrmsrl(MSR_AMD64_LS_CFG, msr); + return; + } + + if (tifn & _TIF_SSBD) { + /* + * Since this can race with prctl(), block reentry on the + * same CPU. + */ + if (__test_and_set_bit(LSTATE_SSB, &st->local_state)) + return; + + msr |= x86_amd_ls_cfg_ssbd_mask; + + raw_spin_lock(&st->shared_state->lock); + /* First sibling enables SSBD: */ + if (!st->shared_state->disable_state) + wrmsrl(MSR_AMD64_LS_CFG, msr); + st->shared_state->disable_state++; + raw_spin_unlock(&st->shared_state->lock); + } else { + if (!__test_and_clear_bit(LSTATE_SSB, &st->local_state)) + return; + + raw_spin_lock(&st->shared_state->lock); + st->shared_state->disable_state--; + if (!st->shared_state->disable_state) + wrmsrl(MSR_AMD64_LS_CFG, msr); + raw_spin_unlock(&st->shared_state->lock); + } +} +#else +static __always_inline void amd_set_core_ssb_state(unsigned long tifn) +{ + u64 msr = x86_amd_ls_cfg_base | ssbd_tif_to_amd_ls_cfg(tifn); + + wrmsrl(MSR_AMD64_LS_CFG, msr); +} +#endif + +static __always_inline void amd_set_ssb_virt_state(unsigned long tifn) +{ + /* + * SSBD has the same definition in SPEC_CTRL and VIRT_SPEC_CTRL, + * so ssbd_tif_to_spec_ctrl() just works. + */ + wrmsrl(MSR_AMD64_VIRT_SPEC_CTRL, ssbd_tif_to_spec_ctrl(tifn)); +} + +static __always_inline void intel_set_ssb_state(unsigned long tifn) +{ + u64 msr = x86_spec_ctrl_base | ssbd_tif_to_spec_ctrl(tifn); + + wrmsrl(MSR_IA32_SPEC_CTRL, msr); +} + +static __always_inline void __speculative_store_bypass_update(unsigned long tifn) +{ + if (static_cpu_has(X86_FEATURE_VIRT_SSBD)) + amd_set_ssb_virt_state(tifn); + else if (static_cpu_has(X86_FEATURE_LS_CFG_SSBD)) + amd_set_core_ssb_state(tifn); + else + intel_set_ssb_state(tifn); +} + +void speculative_store_bypass_update(unsigned long tif) +{ + preempt_disable(); + __speculative_store_bypass_update(tif); + preempt_enable(); +} + void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, struct tss_struct *tss) { @@ -309,6 +452,9 @@ void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p, if ((tifp ^ tifn) & _TIF_NOCPUID) set_cpuid_faulting(!!(tifn & _TIF_NOCPUID)); + + if ((tifp ^ tifn) & _TIF_SSBD) + __speculative_store_bypass_update(tifn); } /* diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 4b100fe0f5087f..12bb445fb98d66 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -542,6 +542,7 @@ void set_personality_64bit(void) clear_thread_flag(TIF_X32); /* Pretend that this comes from a 64bit execve */ task_pt_regs(current)->orig_ax = __NR_execve; + current_thread_info()->status &= ~TS_COMPAT; /* Ensure the corresponding mm is not marked. */ if (current->mm) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 0f1cbb042f49b8..9dd324ae483291 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -79,6 +79,7 @@ #include #include #include +#include /* Number of siblings per CPU package */ int smp_num_siblings = 1; @@ -244,6 +245,8 @@ static void notrace start_secondary(void *unused) */ check_tsc_sync_target(); + speculative_store_bypass_ht_init(); + /* * Lock vector_lock, set CPU online and bring the vector * allocator online. Online must be set with vector_lock held @@ -1292,6 +1295,8 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus) set_mtrr_aps_delayed_init(); smp_quirk_init_udelay(); + + speculative_store_bypass_ht_init(); } void arch_enable_nonboot_cpus_begin(void) diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 85c7ef23d99f7f..c84bb539695828 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -299,6 +299,10 @@ static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool if (is_prefix_bad(insn)) return -ENOTSUPP; + /* We should not singlestep on the exception masking instructions */ + if (insn_masking_exception(insn)) + return -ENOTSUPP; + if (x86_64) good_insns = good_insns_64; else diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 82055b90a8b314..92bf2f2e7cdd24 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -379,7 +379,7 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, /* cpuid 0x80000008.ebx */ const u32 kvm_cpuid_8000_0008_ebx_x86_features = - F(IBPB) | F(IBRS); + F(AMD_IBPB) | F(AMD_IBRS) | F(VIRT_SSBD); /* cpuid 0xC0000001.edx */ const u32 kvm_cpuid_C000_0001_edx_x86_features = @@ -408,7 +408,7 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, /* cpuid 7.0.edx*/ const u32 kvm_cpuid_7_0_edx_x86_features = F(AVX512_4VNNIW) | F(AVX512_4FMAPS) | F(SPEC_CTRL) | - F(ARCH_CAPABILITIES); + F(SPEC_CTRL_SSBD) | F(ARCH_CAPABILITIES); /* all calls to cpuid_count() should be made on the same cpu */ get_cpu(); @@ -495,6 +495,11 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, entry->ecx &= ~F(PKU); entry->edx &= kvm_cpuid_7_0_edx_x86_features; cpuid_mask(&entry->edx, CPUID_7_EDX); + /* + * We emulate ARCH_CAPABILITIES in software even + * if the host doesn't support it. + */ + entry->edx |= F(ARCH_CAPABILITIES); } else { entry->ebx = 0; entry->ecx = 0; @@ -647,13 +652,20 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, g_phys_as = phys_as; entry->eax = g_phys_as | (virt_as << 8); entry->edx = 0; - /* IBRS and IBPB aren't necessarily present in hardware cpuid */ - if (boot_cpu_has(X86_FEATURE_IBPB)) - entry->ebx |= F(IBPB); - if (boot_cpu_has(X86_FEATURE_IBRS)) - entry->ebx |= F(IBRS); + /* + * IBRS, IBPB and VIRT_SSBD aren't necessarily present in + * hardware cpuid + */ + if (boot_cpu_has(X86_FEATURE_AMD_IBPB)) + entry->ebx |= F(AMD_IBPB); + if (boot_cpu_has(X86_FEATURE_AMD_IBRS)) + entry->ebx |= F(AMD_IBRS); + if (boot_cpu_has(X86_FEATURE_VIRT_SSBD)) + entry->ebx |= F(VIRT_SSBD); entry->ebx &= kvm_cpuid_8000_0008_ebx_x86_features; cpuid_mask(&entry->ebx, CPUID_8000_0008_EBX); + if (boot_cpu_has(X86_FEATURE_LS_CFG_SSBD)) + entry->ebx |= F(VIRT_SSBD); break; } case 0x80000019: diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index b3705ae5282490..4c4f4263420c05 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -812,6 +812,19 @@ static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel) return assign_eip_near(ctxt, ctxt->_eip + rel); } +static int linear_read_system(struct x86_emulate_ctxt *ctxt, ulong linear, + void *data, unsigned size) +{ + return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception, true); +} + +static int linear_write_system(struct x86_emulate_ctxt *ctxt, + ulong linear, void *data, + unsigned int size) +{ + return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception, true); +} + static int segmented_read_std(struct x86_emulate_ctxt *ctxt, struct segmented_address addr, void *data, @@ -823,7 +836,7 @@ static int segmented_read_std(struct x86_emulate_ctxt *ctxt, rc = linearize(ctxt, addr, size, false, &linear); if (rc != X86EMUL_CONTINUE) return rc; - return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception); + return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception, false); } static int segmented_write_std(struct x86_emulate_ctxt *ctxt, @@ -837,7 +850,7 @@ static int segmented_write_std(struct x86_emulate_ctxt *ctxt, rc = linearize(ctxt, addr, size, true, &linear); if (rc != X86EMUL_CONTINUE) return rc; - return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception); + return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception, false); } /* @@ -1496,8 +1509,7 @@ static int read_interrupt_descriptor(struct x86_emulate_ctxt *ctxt, return emulate_gp(ctxt, index << 3 | 0x2); addr = dt.address + index * 8; - return ctxt->ops->read_std(ctxt, addr, desc, sizeof *desc, - &ctxt->exception); + return linear_read_system(ctxt, addr, desc, sizeof *desc); } static void get_descriptor_table_ptr(struct x86_emulate_ctxt *ctxt, @@ -1560,8 +1572,7 @@ static int read_segment_descriptor(struct x86_emulate_ctxt *ctxt, if (rc != X86EMUL_CONTINUE) return rc; - return ctxt->ops->read_std(ctxt, *desc_addr_p, desc, sizeof(*desc), - &ctxt->exception); + return linear_read_system(ctxt, *desc_addr_p, desc, sizeof(*desc)); } /* allowed just for 8 bytes segments */ @@ -1575,8 +1586,7 @@ static int write_segment_descriptor(struct x86_emulate_ctxt *ctxt, if (rc != X86EMUL_CONTINUE) return rc; - return ctxt->ops->write_std(ctxt, addr, desc, sizeof *desc, - &ctxt->exception); + return linear_write_system(ctxt, addr, desc, sizeof *desc); } static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, @@ -1737,8 +1747,7 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, return ret; } } else if (ctxt->mode == X86EMUL_MODE_PROT64) { - ret = ctxt->ops->read_std(ctxt, desc_addr+8, &base3, - sizeof(base3), &ctxt->exception); + ret = linear_read_system(ctxt, desc_addr+8, &base3, sizeof(base3)); if (ret != X86EMUL_CONTINUE) return ret; if (emul_is_noncanonical_address(get_desc_base(&seg_desc) | @@ -2051,11 +2060,11 @@ static int __emulate_int_real(struct x86_emulate_ctxt *ctxt, int irq) eip_addr = dt.address + (irq << 2); cs_addr = dt.address + (irq << 2) + 2; - rc = ops->read_std(ctxt, cs_addr, &cs, 2, &ctxt->exception); + rc = linear_read_system(ctxt, cs_addr, &cs, 2); if (rc != X86EMUL_CONTINUE) return rc; - rc = ops->read_std(ctxt, eip_addr, &eip, 2, &ctxt->exception); + rc = linear_read_system(ctxt, eip_addr, &eip, 2); if (rc != X86EMUL_CONTINUE) return rc; @@ -2919,12 +2928,12 @@ static bool emulator_io_port_access_allowed(struct x86_emulate_ctxt *ctxt, #ifdef CONFIG_X86_64 base |= ((u64)base3) << 32; #endif - r = ops->read_std(ctxt, base + 102, &io_bitmap_ptr, 2, NULL); + r = ops->read_std(ctxt, base + 102, &io_bitmap_ptr, 2, NULL, true); if (r != X86EMUL_CONTINUE) return false; if (io_bitmap_ptr + port/8 > desc_limit_scaled(&tr_seg)) return false; - r = ops->read_std(ctxt, base + io_bitmap_ptr + port/8, &perm, 2, NULL); + r = ops->read_std(ctxt, base + io_bitmap_ptr + port/8, &perm, 2, NULL, true); if (r != X86EMUL_CONTINUE) return false; if ((perm >> bit_idx) & mask) @@ -3053,35 +3062,30 @@ static int task_switch_16(struct x86_emulate_ctxt *ctxt, u16 tss_selector, u16 old_tss_sel, ulong old_tss_base, struct desc_struct *new_desc) { - const struct x86_emulate_ops *ops = ctxt->ops; struct tss_segment_16 tss_seg; int ret; u32 new_tss_base = get_desc_base(new_desc); - ret = ops->read_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg, - &ctxt->exception); + ret = linear_read_system(ctxt, old_tss_base, &tss_seg, sizeof tss_seg); if (ret != X86EMUL_CONTINUE) return ret; save_state_to_tss16(ctxt, &tss_seg); - ret = ops->write_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg, - &ctxt->exception); + ret = linear_write_system(ctxt, old_tss_base, &tss_seg, sizeof tss_seg); if (ret != X86EMUL_CONTINUE) return ret; - ret = ops->read_std(ctxt, new_tss_base, &tss_seg, sizeof tss_seg, - &ctxt->exception); + ret = linear_read_system(ctxt, new_tss_base, &tss_seg, sizeof tss_seg); if (ret != X86EMUL_CONTINUE) return ret; if (old_tss_sel != 0xffff) { tss_seg.prev_task_link = old_tss_sel; - ret = ops->write_std(ctxt, new_tss_base, - &tss_seg.prev_task_link, - sizeof tss_seg.prev_task_link, - &ctxt->exception); + ret = linear_write_system(ctxt, new_tss_base, + &tss_seg.prev_task_link, + sizeof tss_seg.prev_task_link); if (ret != X86EMUL_CONTINUE) return ret; } @@ -3197,38 +3201,34 @@ static int task_switch_32(struct x86_emulate_ctxt *ctxt, u16 tss_selector, u16 old_tss_sel, ulong old_tss_base, struct desc_struct *new_desc) { - const struct x86_emulate_ops *ops = ctxt->ops; struct tss_segment_32 tss_seg; int ret; u32 new_tss_base = get_desc_base(new_desc); u32 eip_offset = offsetof(struct tss_segment_32, eip); u32 ldt_sel_offset = offsetof(struct tss_segment_32, ldt_selector); - ret = ops->read_std(ctxt, old_tss_base, &tss_seg, sizeof tss_seg, - &ctxt->exception); + ret = linear_read_system(ctxt, old_tss_base, &tss_seg, sizeof tss_seg); if (ret != X86EMUL_CONTINUE) return ret; save_state_to_tss32(ctxt, &tss_seg); /* Only GP registers and segment selectors are saved */ - ret = ops->write_std(ctxt, old_tss_base + eip_offset, &tss_seg.eip, - ldt_sel_offset - eip_offset, &ctxt->exception); + ret = linear_write_system(ctxt, old_tss_base + eip_offset, &tss_seg.eip, + ldt_sel_offset - eip_offset); if (ret != X86EMUL_CONTINUE) return ret; - ret = ops->read_std(ctxt, new_tss_base, &tss_seg, sizeof tss_seg, - &ctxt->exception); + ret = linear_read_system(ctxt, new_tss_base, &tss_seg, sizeof tss_seg); if (ret != X86EMUL_CONTINUE) return ret; if (old_tss_sel != 0xffff) { tss_seg.prev_task_link = old_tss_sel; - ret = ops->write_std(ctxt, new_tss_base, - &tss_seg.prev_task_link, - sizeof tss_seg.prev_task_link, - &ctxt->exception); + ret = linear_write_system(ctxt, new_tss_base, + &tss_seg.prev_task_link, + sizeof tss_seg.prev_task_link); if (ret != X86EMUL_CONTINUE) return ret; } @@ -4189,7 +4189,9 @@ static int check_cr_write(struct x86_emulate_ctxt *ctxt) maxphyaddr = eax & 0xff; else maxphyaddr = 36; - rsvd = rsvd_bits(maxphyaddr, 62); + rsvd = rsvd_bits(maxphyaddr, 63); + if (ctxt->ops->get_cr(ctxt, 4) & X86_CR4_PCIDE) + rsvd &= ~CR3_PCID_INVD; } if (new_val & rsvd) diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index 98618e39734229..46ff64da44cab4 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -1260,12 +1260,16 @@ static void kvm_hv_hypercall_set_result(struct kvm_vcpu *vcpu, u64 result) } } -static int kvm_hv_hypercall_complete_userspace(struct kvm_vcpu *vcpu) +static int kvm_hv_hypercall_complete(struct kvm_vcpu *vcpu, u64 result) { - struct kvm_run *run = vcpu->run; + kvm_hv_hypercall_set_result(vcpu, result); + ++vcpu->stat.hypercalls; + return kvm_skip_emulated_instruction(vcpu); +} - kvm_hv_hypercall_set_result(vcpu, run->hyperv.u.hcall.result); - return 1; +static int kvm_hv_hypercall_complete_userspace(struct kvm_vcpu *vcpu) +{ + return kvm_hv_hypercall_complete(vcpu, vcpu->run->hyperv.u.hcall.result); } static u16 kvm_hvcall_signal_event(struct kvm_vcpu *vcpu, bool fast, u64 param) @@ -1296,8 +1300,10 @@ static u16 kvm_hvcall_signal_event(struct kvm_vcpu *vcpu, bool fast, u64 param) if (param & ~KVM_HYPERV_CONN_ID_MASK) return HV_STATUS_INVALID_HYPERCALL_INPUT; - /* conn_to_evt is protected by vcpu->kvm->srcu */ + /* the eventfd is protected by vcpu->kvm->srcu, but conn_to_evt isn't */ + rcu_read_lock(); eventfd = idr_find(&vcpu->kvm->arch.hyperv.conn_to_evt, param); + rcu_read_unlock(); if (!eventfd) return HV_STATUS_INVALID_PORT_ID; @@ -1348,7 +1354,7 @@ int kvm_hv_hypercall(struct kvm_vcpu *vcpu) /* Hypercall continuation is not supported yet */ if (rep_cnt || rep_idx) { ret = HV_STATUS_INVALID_HYPERCALL_CODE; - goto set_result; + goto out; } switch (code) { @@ -1379,9 +1385,8 @@ int kvm_hv_hypercall(struct kvm_vcpu *vcpu) break; } -set_result: - kvm_hv_hypercall_set_result(vcpu, ret); - return 1; +out: + return kvm_hv_hypercall_complete(vcpu, ret); } void kvm_hv_init_vm(struct kvm *kvm) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index b74c9c1405b997..3773c462511404 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1522,11 +1522,23 @@ static bool set_target_expiration(struct kvm_lapic *apic) static void advance_periodic_target_expiration(struct kvm_lapic *apic) { - apic->lapic_timer.tscdeadline += - nsec_to_cycles(apic->vcpu, apic->lapic_timer.period); + ktime_t now = ktime_get(); + u64 tscl = rdtsc(); + ktime_t delta; + + /* + * Synchronize both deadlines to the same time source or + * differences in the periods (caused by differences in the + * underlying clocks or numerical approximation errors) will + * cause the two to drift apart over time as the errors + * accumulate. + */ apic->lapic_timer.target_expiration = ktime_add_ns(apic->lapic_timer.target_expiration, apic->lapic_timer.period); + delta = ktime_sub(apic->lapic_timer.target_expiration, now); + apic->lapic_timer.tscdeadline = kvm_read_l1_tsc(apic->vcpu, tscl) + + nsec_to_cycles(apic->vcpu, delta); } static void start_sw_period(struct kvm_lapic *apic) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 1fc05e428aba82..26110c202b19c4 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include #include "trace.h" @@ -213,6 +213,12 @@ struct vcpu_svm { } host; u64 spec_ctrl; + /* + * Contains guest-controlled bits of VIRT_SPEC_CTRL, which will be + * translated into the appropriate L2_CFG bits on the host to + * perform speculative control. + */ + u64 virt_spec_ctrl; u32 *msrpm; @@ -2060,6 +2066,7 @@ static void svm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) vcpu->arch.microcode_version = 0x01000065; svm->spec_ctrl = 0; + svm->virt_spec_ctrl = 0; if (!init_event) { svm->vcpu.arch.apic_base = APIC_DEFAULT_PHYS_BASE | @@ -4108,11 +4115,18 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) break; case MSR_IA32_SPEC_CTRL: if (!msr_info->host_initiated && - !guest_cpuid_has(vcpu, X86_FEATURE_IBRS)) + !guest_cpuid_has(vcpu, X86_FEATURE_AMD_IBRS)) return 1; msr_info->data = svm->spec_ctrl; break; + case MSR_AMD64_VIRT_SPEC_CTRL: + if (!msr_info->host_initiated && + !guest_cpuid_has(vcpu, X86_FEATURE_VIRT_SSBD)) + return 1; + + msr_info->data = svm->virt_spec_ctrl; + break; case MSR_F15H_IC_CFG: { int family, model; @@ -4203,7 +4217,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) break; case MSR_IA32_SPEC_CTRL: if (!msr->host_initiated && - !guest_cpuid_has(vcpu, X86_FEATURE_IBRS)) + !guest_cpuid_has(vcpu, X86_FEATURE_AMD_IBRS)) return 1; /* The STIBP bit doesn't fault even if it's not advertised */ @@ -4230,7 +4244,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) break; case MSR_IA32_PRED_CMD: if (!msr->host_initiated && - !guest_cpuid_has(vcpu, X86_FEATURE_IBPB)) + !guest_cpuid_has(vcpu, X86_FEATURE_AMD_IBPB)) return 1; if (data & ~PRED_CMD_IBPB) @@ -4244,6 +4258,16 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) break; set_msr_interception(svm->msrpm, MSR_IA32_PRED_CMD, 0, 1); break; + case MSR_AMD64_VIRT_SPEC_CTRL: + if (!msr->host_initiated && + !guest_cpuid_has(vcpu, X86_FEATURE_VIRT_SSBD)) + return 1; + + if (data & ~SPEC_CTRL_SSBD) + return 1; + + svm->virt_spec_ctrl = data; + break; case MSR_STAR: svm->vmcb->save.star = data; break; @@ -5557,8 +5581,7 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu) * is no need to worry about the conditional branch over the wrmsr * being speculatively taken. */ - if (svm->spec_ctrl) - native_wrmsrl(MSR_IA32_SPEC_CTRL, svm->spec_ctrl); + x86_spec_ctrl_set_guest(svm->spec_ctrl, svm->virt_spec_ctrl); asm volatile ( "push %%" _ASM_BP "; \n\t" @@ -5652,6 +5675,18 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu) #endif ); + /* Eliminate branch target predictions from guest mode */ + vmexit_fill_RSB(); + +#ifdef CONFIG_X86_64 + wrmsrl(MSR_GS_BASE, svm->host.gs_base); +#else + loadsegment(fs, svm->host.fs); +#ifndef CONFIG_X86_32_LAZY_GS + loadsegment(gs, svm->host.gs); +#endif +#endif + /* * We do not use IBRS in the kernel. If this vCPU has used the * SPEC_CTRL MSR it may have left it on; save the value and @@ -5670,20 +5705,7 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu) if (unlikely(!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL))) svm->spec_ctrl = native_read_msr(MSR_IA32_SPEC_CTRL); - if (svm->spec_ctrl) - native_wrmsrl(MSR_IA32_SPEC_CTRL, 0); - - /* Eliminate branch target predictions from guest mode */ - vmexit_fill_RSB(); - -#ifdef CONFIG_X86_64 - wrmsrl(MSR_GS_BASE, svm->host.gs_base); -#else - loadsegment(fs, svm->host.fs); -#ifndef CONFIG_X86_32_LAZY_GS - loadsegment(gs, svm->host.gs); -#endif -#endif + x86_spec_ctrl_restore_host(svm->spec_ctrl, svm->virt_spec_ctrl); reload_tss(vcpu); @@ -5786,7 +5808,7 @@ static bool svm_cpu_has_accelerated_tpr(void) return false; } -static bool svm_has_high_real_mode_segbase(void) +static bool svm_has_emulated_msr(int index) { return true; } @@ -7012,7 +7034,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = { .hardware_enable = svm_hardware_enable, .hardware_disable = svm_hardware_disable, .cpu_has_accelerated_tpr = svm_cpu_has_accelerated_tpr, - .cpu_has_high_real_mode_segbase = svm_has_high_real_mode_segbase, + .has_emulated_msr = svm_has_emulated_msr, .vcpu_create = svm_create_vcpu, .vcpu_free = svm_free_vcpu, diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index c7668806163fd5..82f5e915e568de 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -51,7 +51,7 @@ #include #include #include -#include +#include #include #include "trace.h" @@ -1494,6 +1494,12 @@ static inline bool cpu_has_vmx_vmfunc(void) SECONDARY_EXEC_ENABLE_VMFUNC; } +static bool vmx_umip_emulated(void) +{ + return vmcs_config.cpu_based_2nd_exec_ctrl & + SECONDARY_EXEC_DESC; +} + static inline bool report_flexpriority(void) { return flexpriority_enabled; @@ -3523,7 +3529,6 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return kvm_get_msr_common(vcpu, msr_info); case MSR_IA32_SPEC_CTRL: if (!msr_info->host_initiated && - !guest_cpuid_has(vcpu, X86_FEATURE_IBRS) && !guest_cpuid_has(vcpu, X86_FEATURE_SPEC_CTRL)) return 1; @@ -3642,12 +3647,11 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) break; case MSR_IA32_SPEC_CTRL: if (!msr_info->host_initiated && - !guest_cpuid_has(vcpu, X86_FEATURE_IBRS) && !guest_cpuid_has(vcpu, X86_FEATURE_SPEC_CTRL)) return 1; /* The STIBP bit doesn't fault even if it's not advertised */ - if (data & ~(SPEC_CTRL_IBRS | SPEC_CTRL_STIBP)) + if (data & ~(SPEC_CTRL_IBRS | SPEC_CTRL_STIBP | SPEC_CTRL_SSBD)) return 1; vmx->spec_ctrl = data; @@ -3673,7 +3677,6 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) break; case MSR_IA32_PRED_CMD: if (!msr_info->host_initiated && - !guest_cpuid_has(vcpu, X86_FEATURE_IBPB) && !guest_cpuid_has(vcpu, X86_FEATURE_SPEC_CTRL)) return 1; @@ -4761,14 +4764,16 @@ static int vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) else hw_cr4 |= KVM_PMODE_VM_CR4_ALWAYS_ON; - if ((cr4 & X86_CR4_UMIP) && !boot_cpu_has(X86_FEATURE_UMIP)) { - vmcs_set_bits(SECONDARY_VM_EXEC_CONTROL, - SECONDARY_EXEC_DESC); - hw_cr4 &= ~X86_CR4_UMIP; - } else if (!is_guest_mode(vcpu) || - !nested_cpu_has2(get_vmcs12(vcpu), SECONDARY_EXEC_DESC)) - vmcs_clear_bits(SECONDARY_VM_EXEC_CONTROL, + if (!boot_cpu_has(X86_FEATURE_UMIP) && vmx_umip_emulated()) { + if (cr4 & X86_CR4_UMIP) { + vmcs_set_bits(SECONDARY_VM_EXEC_CONTROL, SECONDARY_EXEC_DESC); + hw_cr4 &= ~X86_CR4_UMIP; + } else if (!is_guest_mode(vcpu) || + !nested_cpu_has2(get_vmcs12(vcpu), SECONDARY_EXEC_DESC)) + vmcs_clear_bits(SECONDARY_VM_EXEC_CONTROL, + SECONDARY_EXEC_DESC); + } if (cr4 & X86_CR4_VMXE) { /* @@ -7583,8 +7588,7 @@ static int nested_vmx_get_vmptr(struct kvm_vcpu *vcpu, gpa_t *vmpointer) vmcs_read32(VMX_INSTRUCTION_INFO), false, &gva)) return 1; - if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, vmpointer, - sizeof(*vmpointer), &e)) { + if (kvm_read_guest_virt(vcpu, gva, vmpointer, sizeof(*vmpointer), &e)) { kvm_inject_page_fault(vcpu, &e); return 1; } @@ -7665,6 +7669,12 @@ static int handle_vmon(struct kvm_vcpu *vcpu) return 1; } + /* CPL=0 must be checked manually. */ + if (vmx_get_cpl(vcpu)) { + kvm_queue_exception(vcpu, UD_VECTOR); + return 1; + } + if (vmx->nested.vmxon) { nested_vmx_failValid(vcpu, VMXERR_VMXON_IN_VMX_ROOT_OPERATION); return kvm_skip_emulated_instruction(vcpu); @@ -7724,6 +7734,11 @@ static int handle_vmon(struct kvm_vcpu *vcpu) */ static int nested_vmx_check_permission(struct kvm_vcpu *vcpu) { + if (vmx_get_cpl(vcpu)) { + kvm_queue_exception(vcpu, UD_VECTOR); + return 0; + } + if (!to_vmx(vcpu)->nested.vmxon) { kvm_queue_exception(vcpu, UD_VECTOR); return 0; @@ -8024,9 +8039,9 @@ static int handle_vmread(struct kvm_vcpu *vcpu) if (get_vmx_mem_address(vcpu, exit_qualification, vmx_instruction_info, true, &gva)) return 1; - /* _system ok, as hardware has verified cpl=0 */ - kvm_write_guest_virt_system(&vcpu->arch.emulate_ctxt, gva, - &field_value, (is_long_mode(vcpu) ? 8 : 4), NULL); + /* _system ok, nested_vmx_check_permission has verified cpl=0 */ + kvm_write_guest_virt_system(vcpu, gva, &field_value, + (is_long_mode(vcpu) ? 8 : 4), NULL); } nested_vmx_succeed(vcpu); @@ -8064,8 +8079,8 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu) if (get_vmx_mem_address(vcpu, exit_qualification, vmx_instruction_info, false, &gva)) return 1; - if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, - &field_value, (is_64_bit_mode(vcpu) ? 8 : 4), &e)) { + if (kvm_read_guest_virt(vcpu, gva, &field_value, + (is_64_bit_mode(vcpu) ? 8 : 4), &e)) { kvm_inject_page_fault(vcpu, &e); return 1; } @@ -8184,10 +8199,10 @@ static int handle_vmptrst(struct kvm_vcpu *vcpu) if (get_vmx_mem_address(vcpu, exit_qualification, vmx_instruction_info, true, &vmcs_gva)) return 1; - /* ok to use *_system, as hardware has verified cpl=0 */ - if (kvm_write_guest_virt_system(&vcpu->arch.emulate_ctxt, vmcs_gva, - (void *)&to_vmx(vcpu)->nested.current_vmptr, - sizeof(u64), &e)) { + /* *_system ok, nested_vmx_check_permission has verified cpl=0 */ + if (kvm_write_guest_virt_system(vcpu, vmcs_gva, + (void *)&to_vmx(vcpu)->nested.current_vmptr, + sizeof(u64), &e)) { kvm_inject_page_fault(vcpu, &e); return 1; } @@ -8234,8 +8249,7 @@ static int handle_invept(struct kvm_vcpu *vcpu) if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION), vmx_instruction_info, false, &gva)) return 1; - if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &operand, - sizeof(operand), &e)) { + if (kvm_read_guest_virt(vcpu, gva, &operand, sizeof(operand), &e)) { kvm_inject_page_fault(vcpu, &e); return 1; } @@ -8299,8 +8313,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu) if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION), vmx_instruction_info, false, &gva)) return 1; - if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &operand, - sizeof(operand), &e)) { + if (kvm_read_guest_virt(vcpu, gva, &operand, sizeof(operand), &e)) { kvm_inject_page_fault(vcpu, &e); return 1; } @@ -9480,9 +9493,21 @@ static void vmx_handle_external_intr(struct kvm_vcpu *vcpu) } STACK_FRAME_NON_STANDARD(vmx_handle_external_intr); -static bool vmx_has_high_real_mode_segbase(void) +static bool vmx_has_emulated_msr(int index) { - return enable_unrestricted_guest || emulate_invalid_guest_state; + switch (index) { + case MSR_IA32_SMBASE: + /* + * We cannot do SMM unless we can run the guest in big + * real mode. + */ + return enable_unrestricted_guest || emulate_invalid_guest_state; + case MSR_AMD64_VIRT_SPEC_CTRL: + /* This is AMD only. */ + return false; + default: + return true; + } } static bool vmx_mpx_supported(void) @@ -9497,12 +9522,6 @@ static bool vmx_xsaves_supported(void) SECONDARY_EXEC_XSAVES; } -static bool vmx_umip_emulated(void) -{ - return vmcs_config.cpu_based_2nd_exec_ctrl & - SECONDARY_EXEC_DESC; -} - static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx) { u32 exit_intr_info; @@ -9720,8 +9739,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) * is no need to worry about the conditional branch over the wrmsr * being speculatively taken. */ - if (vmx->spec_ctrl) - native_wrmsrl(MSR_IA32_SPEC_CTRL, vmx->spec_ctrl); + x86_spec_ctrl_set_guest(vmx->spec_ctrl, 0); vmx->__launched = vmx->loaded_vmcs->launched; @@ -9869,8 +9887,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) if (unlikely(!msr_write_intercepted(vcpu, MSR_IA32_SPEC_CTRL))) vmx->spec_ctrl = native_read_msr(MSR_IA32_SPEC_CTRL); - if (vmx->spec_ctrl) - native_wrmsrl(MSR_IA32_SPEC_CTRL, 0); + x86_spec_ctrl_restore_host(vmx->spec_ctrl, 0); /* Eliminate branch target predictions from guest mode */ vmexit_fill_RSB(); @@ -12630,7 +12647,7 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = { .hardware_enable = hardware_enable, .hardware_disable = hardware_disable, .cpu_has_accelerated_tpr = report_flexpriority, - .cpu_has_high_real_mode_segbase = vmx_has_high_real_mode_segbase, + .has_emulated_msr = vmx_has_emulated_msr, .vm_init = vmx_vm_init, .vm_alloc = vmx_vm_alloc, diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 51ecd381793b4f..fbc4d17e3ecc80 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -114,7 +114,7 @@ module_param(ignore_msrs, bool, S_IRUGO | S_IWUSR); static bool __read_mostly report_ignored_msrs = true; module_param(report_ignored_msrs, bool, S_IRUGO | S_IWUSR); -unsigned int min_timer_period_us = 500; +unsigned int min_timer_period_us = 200; module_param(min_timer_period_us, uint, S_IRUGO | S_IWUSR); static bool __read_mostly kvmclock_periodic_sync = true; @@ -843,7 +843,10 @@ EXPORT_SYMBOL_GPL(kvm_set_cr4); int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3) { #ifdef CONFIG_X86_64 - cr3 &= ~CR3_PCID_INVD; + bool pcid_enabled = kvm_read_cr4_bits(vcpu, X86_CR4_PCIDE); + + if (pcid_enabled) + cr3 &= ~CR3_PCID_INVD; #endif if (cr3 == kvm_read_cr3(vcpu) && !pdptrs_changed(vcpu)) { @@ -853,7 +856,7 @@ int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3) } if (is_long_mode(vcpu) && - (cr3 & rsvd_bits(cpuid_maxphyaddr(vcpu), 62))) + (cr3 & rsvd_bits(cpuid_maxphyaddr(vcpu), 63))) return 1; else if (is_pae(vcpu) && is_paging(vcpu) && !load_pdptrs(vcpu, vcpu->arch.walk_mmu, cr3)) @@ -1058,6 +1061,7 @@ static u32 emulated_msrs[] = { MSR_SMI_COUNT, MSR_PLATFORM_INFO, MSR_MISC_FEATURES_ENABLES, + MSR_AMD64_VIRT_SPEC_CTRL, }; static unsigned num_emulated_msrs; @@ -2890,7 +2894,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) r = KVM_CLOCK_TSC_STABLE; break; case KVM_CAP_X86_DISABLE_EXITS: - r |= KVM_X86_DISABLE_EXITS_HTL | KVM_X86_DISABLE_EXITS_PAUSE; + r |= KVM_X86_DISABLE_EXITS_HLT | KVM_X86_DISABLE_EXITS_PAUSE; if(kvm_can_mwait_in_guest()) r |= KVM_X86_DISABLE_EXITS_MWAIT; break; @@ -2903,7 +2907,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) * fringe case that is not enabled except via specific settings * of the module parameters. */ - r = kvm_x86_ops->cpu_has_high_real_mode_segbase(); + r = kvm_x86_ops->has_emulated_msr(MSR_IA32_SMBASE); break; case KVM_CAP_VAPIC: r = !kvm_x86_ops->cpu_has_accelerated_tpr(); @@ -4244,7 +4248,7 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, if ((cap->args[0] & KVM_X86_DISABLE_EXITS_MWAIT) && kvm_can_mwait_in_guest()) kvm->arch.mwait_in_guest = true; - if (cap->args[0] & KVM_X86_DISABLE_EXITS_HTL) + if (cap->args[0] & KVM_X86_DISABLE_EXITS_HLT) kvm->arch.hlt_in_guest = true; if (cap->args[0] & KVM_X86_DISABLE_EXITS_PAUSE) kvm->arch.pause_in_guest = true; @@ -4603,14 +4607,8 @@ static void kvm_init_msr_list(void) num_msrs_to_save = j; for (i = j = 0; i < ARRAY_SIZE(emulated_msrs); i++) { - switch (emulated_msrs[i]) { - case MSR_IA32_SMBASE: - if (!kvm_x86_ops->cpu_has_high_real_mode_segbase()) - continue; - break; - default: - break; - } + if (!kvm_x86_ops->has_emulated_msr(emulated_msrs[i])) + continue; if (j < i) emulated_msrs[j] = emulated_msrs[i]; @@ -4789,11 +4787,10 @@ static int kvm_fetch_guest_virt(struct x86_emulate_ctxt *ctxt, return X86EMUL_CONTINUE; } -int kvm_read_guest_virt(struct x86_emulate_ctxt *ctxt, +int kvm_read_guest_virt(struct kvm_vcpu *vcpu, gva_t addr, void *val, unsigned int bytes, struct x86_exception *exception) { - struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0; return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, access, @@ -4801,12 +4798,17 @@ int kvm_read_guest_virt(struct x86_emulate_ctxt *ctxt, } EXPORT_SYMBOL_GPL(kvm_read_guest_virt); -static int kvm_read_guest_virt_system(struct x86_emulate_ctxt *ctxt, - gva_t addr, void *val, unsigned int bytes, - struct x86_exception *exception) +static int emulator_read_std(struct x86_emulate_ctxt *ctxt, + gva_t addr, void *val, unsigned int bytes, + struct x86_exception *exception, bool system) { struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); - return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, 0, exception); + u32 access = 0; + + if (!system && kvm_x86_ops->get_cpl(vcpu) == 3) + access |= PFERR_USER_MASK; + + return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, access, exception); } static int kvm_read_guest_phys_system(struct x86_emulate_ctxt *ctxt, @@ -4818,18 +4820,16 @@ static int kvm_read_guest_phys_system(struct x86_emulate_ctxt *ctxt, return r < 0 ? X86EMUL_IO_NEEDED : X86EMUL_CONTINUE; } -int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt, - gva_t addr, void *val, - unsigned int bytes, - struct x86_exception *exception) +static int kvm_write_guest_virt_helper(gva_t addr, void *val, unsigned int bytes, + struct kvm_vcpu *vcpu, u32 access, + struct x86_exception *exception) { - struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); void *data = val; int r = X86EMUL_CONTINUE; while (bytes) { gpa_t gpa = vcpu->arch.walk_mmu->gva_to_gpa(vcpu, addr, - PFERR_WRITE_MASK, + access, exception); unsigned offset = addr & (PAGE_SIZE-1); unsigned towrite = min(bytes, (unsigned)PAGE_SIZE - offset); @@ -4850,6 +4850,27 @@ int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt, out: return r; } + +static int emulator_write_std(struct x86_emulate_ctxt *ctxt, gva_t addr, void *val, + unsigned int bytes, struct x86_exception *exception, + bool system) +{ + struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); + u32 access = PFERR_WRITE_MASK; + + if (!system && kvm_x86_ops->get_cpl(vcpu) == 3) + access |= PFERR_USER_MASK; + + return kvm_write_guest_virt_helper(addr, val, bytes, vcpu, + access, exception); +} + +int kvm_write_guest_virt_system(struct kvm_vcpu *vcpu, gva_t addr, void *val, + unsigned int bytes, struct x86_exception *exception) +{ + return kvm_write_guest_virt_helper(addr, val, bytes, vcpu, + PFERR_WRITE_MASK, exception); +} EXPORT_SYMBOL_GPL(kvm_write_guest_virt_system); int handle_ud(struct kvm_vcpu *vcpu) @@ -4860,8 +4881,8 @@ int handle_ud(struct kvm_vcpu *vcpu) struct x86_exception e; if (force_emulation_prefix && - kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, - kvm_get_linear_rip(vcpu), sig, sizeof(sig), &e) == 0 && + kvm_read_guest_virt(vcpu, kvm_get_linear_rip(vcpu), + sig, sizeof(sig), &e) == 0 && memcmp(sig, "\xf\xbkvm", sizeof(sig)) == 0) { kvm_rip_write(vcpu, kvm_rip_read(vcpu) + sizeof(sig)); emul_type = 0; @@ -5602,8 +5623,8 @@ static int emulator_pre_leave_smm(struct x86_emulate_ctxt *ctxt, u64 smbase) static const struct x86_emulate_ops emulate_ops = { .read_gpr = emulator_read_gpr, .write_gpr = emulator_write_gpr, - .read_std = kvm_read_guest_virt_system, - .write_std = kvm_write_guest_virt_system, + .read_std = emulator_read_std, + .write_std = emulator_write_std, .read_phys = kvm_read_guest_phys_system, .fetch = kvm_fetch_guest_virt, .read_emulated = emulator_read_emulated, @@ -6671,9 +6692,7 @@ void kvm_vcpu_deactivate_apicv(struct kvm_vcpu *vcpu) int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) { unsigned long nr, a0, a1, a2, a3, ret; - int op_64_bit, r; - - r = kvm_skip_emulated_instruction(vcpu); + int op_64_bit; if (kvm_hv_hypercall_enabled(vcpu->kvm)) return kvm_hv_hypercall(vcpu); @@ -6721,8 +6740,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) if (!op_64_bit) ret = (u32)ret; kvm_register_write(vcpu, VCPU_REGS_RAX, ret); + ++vcpu->stat.hypercalls; - return r; + return kvm_skip_emulated_instruction(vcpu); } EXPORT_SYMBOL_GPL(kvm_emulate_hypercall); @@ -7979,6 +7999,7 @@ static int __set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) { struct msr_data apic_base_msr; int mmu_reset_needed = 0; + int cpuid_update_needed = 0; int pending_vec, max_bits, idx; struct desc_ptr dt; int ret = -EINVAL; @@ -8017,8 +8038,10 @@ static int __set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) vcpu->arch.cr0 = sregs->cr0; mmu_reset_needed |= kvm_read_cr4(vcpu) != sregs->cr4; + cpuid_update_needed |= ((kvm_read_cr4(vcpu) ^ sregs->cr4) & + (X86_CR4_OSXSAVE | X86_CR4_PKE)); kvm_x86_ops->set_cr4(vcpu, sregs->cr4); - if (sregs->cr4 & (X86_CR4_OSXSAVE | X86_CR4_PKE)) + if (cpuid_update_needed) kvm_update_cpuid(vcpu); idx = srcu_read_lock(&vcpu->kvm->srcu); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index c9492f7649020e..331993c49dae9b 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -247,11 +247,11 @@ int kvm_inject_realmode_interrupt(struct kvm_vcpu *vcpu, int irq, int inc_eip); void kvm_write_tsc(struct kvm_vcpu *vcpu, struct msr_data *msr); u64 get_kvmclock_ns(struct kvm *kvm); -int kvm_read_guest_virt(struct x86_emulate_ctxt *ctxt, +int kvm_read_guest_virt(struct kvm_vcpu *vcpu, gva_t addr, void *val, unsigned int bytes, struct x86_exception *exception); -int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt, +int kvm_write_guest_virt_system(struct kvm_vcpu *vcpu, gva_t addr, void *val, unsigned int bytes, struct x86_exception *exception); diff --git a/arch/x86/mm/pkeys.c b/arch/x86/mm/pkeys.c index d7bc0eea20a5ed..6e98e0a7c92315 100644 --- a/arch/x86/mm/pkeys.c +++ b/arch/x86/mm/pkeys.c @@ -94,26 +94,27 @@ int __arch_override_mprotect_pkey(struct vm_area_struct *vma, int prot, int pkey */ if (pkey != -1) return pkey; - /* - * Look for a protection-key-drive execute-only mapping - * which is now being given permissions that are not - * execute-only. Move it back to the default pkey. - */ - if (vma_is_pkey_exec_only(vma) && - (prot & (PROT_READ|PROT_WRITE))) { - return 0; - } + /* * The mapping is execute-only. Go try to get the * execute-only protection key. If we fail to do that, * fall through as if we do not have execute-only - * support. + * support in this mm. */ if (prot == PROT_EXEC) { pkey = execute_only_pkey(vma->vm_mm); if (pkey > 0) return pkey; + } else if (vma_is_pkey_exec_only(vma)) { + /* + * Protections are *not* PROT_EXEC, but the mapping + * is using the exec-only pkey. This mapping was + * PROT_EXEC and will no longer be. Move back to + * the default pkey. + */ + return ARCH_DEFAULT_PKEY; } + /* * This is a vanilla, non-pkey mprotect (or we failed to * setup execute-only), inherit the pkey from the VMA we diff --git a/arch/x86/platform/uv/uv_irq.c b/arch/x86/platform/uv/uv_irq.c index e4cb9f4cde8ae2..fc13cbbb2dce21 100644 --- a/arch/x86/platform/uv/uv_irq.c +++ b/arch/x86/platform/uv/uv_irq.c @@ -47,11 +47,6 @@ static void uv_program_mmr(struct irq_cfg *cfg, struct uv_irq_2_mmr_pnode *info) static void uv_noop(struct irq_data *data) { } -static void uv_ack_apic(struct irq_data *data) -{ - ack_APIC_irq(); -} - static int uv_set_irq_affinity(struct irq_data *data, const struct cpumask *mask, bool force) @@ -73,7 +68,7 @@ static struct irq_chip uv_irq_chip = { .name = "UV-CORE", .irq_mask = uv_noop, .irq_unmask = uv_noop, - .irq_eoi = uv_ack_apic, + .irq_eoi = apic_ack_irq, .irq_set_affinity = uv_set_irq_affinity, }; diff --git a/arch/x86/xen/enlighten_hvm.c b/arch/x86/xen/enlighten_hvm.c index 82689870104584..19c1ff54238758 100644 --- a/arch/x86/xen/enlighten_hvm.c +++ b/arch/x86/xen/enlighten_hvm.c @@ -65,6 +65,19 @@ static void __init xen_hvm_init_mem_mapping(void) { early_memunmap(HYPERVISOR_shared_info, PAGE_SIZE); HYPERVISOR_shared_info = __va(PFN_PHYS(shared_info_pfn)); + + /* + * The virtual address of the shared_info page has changed, so + * the vcpu_info pointer for VCPU 0 is now stale. + * + * The prepare_boot_cpu callback will re-initialize it via + * xen_vcpu_setup, but we can't rely on that to be called for + * old Xen versions (xen_have_vector_callback == 0). + * + * It is, in any case, bad to have a stale vcpu_info pointer + * so reset it now. + */ + xen_vcpu_info_reset(0); } static void __init init_hvm_pv_info(void) diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index d33e7dbe3129fc..2d76106788a31c 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -42,13 +42,11 @@ xmaddr_t arbitrary_virt_to_machine(void *vaddr) } EXPORT_SYMBOL_GPL(arbitrary_virt_to_machine); -static void xen_flush_tlb_all(void) +static noinline void xen_flush_tlb_all(void) { struct mmuext_op *op; struct multicall_space mcs; - trace_xen_mmu_flush_tlb_all(0); - preempt_disable(); mcs = xen_mc_entry(sizeof(*op)); diff --git a/arch/x86/xen/mmu_pv.c b/arch/x86/xen/mmu_pv.c index 486c0a34d00b2b..2c30cabfda90fb 100644 --- a/arch/x86/xen/mmu_pv.c +++ b/arch/x86/xen/mmu_pv.c @@ -1310,13 +1310,11 @@ unsigned long xen_read_cr2_direct(void) return this_cpu_read(xen_vcpu_info.arch.cr2); } -static void xen_flush_tlb(void) +static noinline void xen_flush_tlb(void) { struct mmuext_op *op; struct multicall_space mcs; - trace_xen_mmu_flush_tlb(0); - preempt_disable(); mcs = xen_mc_entry(sizeof(*op)); diff --git a/block/blk-mq.c b/block/blk-mq.c index 9ce9cac16c3f5a..90ffd8151c5740 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -2473,7 +2473,6 @@ static void blk_mq_del_queue_tag_set(struct request_queue *q) mutex_lock(&set->tag_list_lock); list_del_rcu(&q->tag_set_list); - INIT_LIST_HEAD(&q->tag_set_list); if (list_is_singular(&set->tag_list)) { /* just transitioned to unshared */ set->flags &= ~BLK_MQ_F_TAG_SHARED; @@ -2481,8 +2480,8 @@ static void blk_mq_del_queue_tag_set(struct request_queue *q) blk_mq_update_tag_set_depth(set, false); } mutex_unlock(&set->tag_list_lock); - synchronize_rcu(); + INIT_LIST_HEAD(&q->tag_set_list); } static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set, diff --git a/block/blk-zoned.c b/block/blk-zoned.c index 08e84ef2bc054d..3d08dc84db1674 100644 --- a/block/blk-zoned.c +++ b/block/blk-zoned.c @@ -328,7 +328,11 @@ int blkdev_report_zones_ioctl(struct block_device *bdev, fmode_t mode, if (!rep.nr_zones) return -EINVAL; - zones = kcalloc(rep.nr_zones, sizeof(struct blk_zone), GFP_KERNEL); + if (rep.nr_zones > INT_MAX / sizeof(struct blk_zone)) + return -ERANGE; + + zones = kvmalloc(rep.nr_zones * sizeof(struct blk_zone), + GFP_KERNEL | __GFP_ZERO); if (!zones) return -ENOMEM; @@ -350,7 +354,7 @@ int blkdev_report_zones_ioctl(struct block_device *bdev, fmode_t mode, } out: - kfree(zones); + kvfree(zones); return ret; } diff --git a/busybox/.gitignore b/busybox/.gitignore new file mode 100644 index 00000000000000..be1d46199f57ed --- /dev/null +++ b/busybox/.gitignore @@ -0,0 +1,52 @@ +# +# Kbuild ignores +# +.* +*.o +*.o.* +*.a +*.s +Kbuild +Config.in + +# +# Never ignore these +# +!.gitignore + +# +# Normal output +# +/busybox +/busybox_old +/busybox_unstripped* + +# +# Backups / patches +# +*~ +*.orig +*.rej +/*.patch + +# +# debugging stuff +# +core +.gdb_history +.gdbinit + +# +# testing output +# +/busybox.links +/runtest-tempdir-links +/testsuite/echo-ne + +# +# cscope output +# +cscope.files +cscope.in.out +cscope.out +cscope.po.out diff --git a/busybox/AUTHORS b/busybox/AUTHORS new file mode 100644 index 00000000000000..5c9a634c926bb8 --- /dev/null +++ b/busybox/AUTHORS @@ -0,0 +1,183 @@ +List of the authors of code contained in BusyBox. + +If you have code in BusyBox, you should be listed here. If you should be +listed, or the description of what you have done needs more detail, or is +incorrect, _please_ let me know. + + -Erik + +----------- + +Peter Willis + eject + +Emanuele Aina + run-parts + +Erik Andersen + Tons of new stuff, major rewrite of most of the + core apps, tons of new apps as noted in header files. + Lots of tedious effort writing these boring docs that + nobody is going to actually read. + +Laurence Anderson + rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm + +Jeff Angielski + ftpput, ftpget + +Enrik Berkhan + setconsole + +Jim Bauer + modprobe shell dependency + +Edward Betts + expr, hostid, logname, whoami + +John Beppu + du, nslookup, sort + +David Brownell + zcip + +Brian Candler + tiny-ls(ls) + +Randolph Chung + fbset, ping, hostname + +Dave Cinege + more(v2), makedevs, dutmp, modularization, auto links file, + various fixes, Linux Router Project maintenance + +Jordan Crouse + ipcalc + +Magnus Damm + tftp client + insmod powerpc support + +Larry Doolittle + pristine source directory compilation, lots of patches and fixes. + +Glenn Engel + httpd + +Gennady Feldman + Sysklogd (single threaded syslogd, IPC Circular buffer support, + logread), various fixes. + +Robert Griebl + modprobe, hwclock, suid/sgid handling, tinylogin integration + many bugfixes and enhancements + +Karl M. Hegbloom + cp_mv.c, the test suite, various fixes to utility.c, &c. + +Daniel Jacobowitz + mktemp.c + +Matt Kraai + documentation, bugfixes, test suite + +Rob Landley + Became busybox maintainer in 2006. + + sed (major rewrite in 2003, and I now maintain the thing) + bunzip2 (complete from-scratch rewrite, then mjn3 optimized the result) + sort (more or less from scratch rewrite in 2004, I now maintain it) + mount (rewrite in 2005, I maintain the new one) + +Stephan Linz + ipcalc, Red Hat equivalence + +John Lombardo + tr + +Glenn McGrath + Common unarchiving code and unarchiving applets, ifupdown, ftpgetput, + nameif, sed, patch, fold, install, uudecode. + Various bugfixes, review and apply numerous patches. + +Manuel Novoa III + cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes, + mesg, vconfig, nice, renice, + make_directory, parse_mode, dirname, mode_string, + get_last_path_component, simplify_path, and a number trivial libbb routines + + also bug fixes, partial rewrites, and size optimizations in + ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir, + mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable, + interface, dutmp, ifconfig, route + +Vladimir Oleynik + cmdedit; bb_mkdep, xargs(current), httpd(current); + ports: ash, crond, fdisk (initial, unmaintained now), inetd, stty, traceroute, + top; + locale, various fixes + and irreconcilable critic of everything not perfect. + +Bruce Perens + Original author of BusyBox in 1995, 1996. Some of his code can + still be found hiding here and there... + +Rodney Radford + ipcs, ipcrm + +Tim Riker + bug fixes, member of fan club + +Kent Robotti + reset, tons and tons of bug reports and patches. + +Chip Rosenthal , + wget - Contributed by permission of Covad Communications + +Pavel Roskin + Lots of bugs fixes and patches. + +Gyepi Sam + Remote logging feature for syslogd + +Rob Sullivan + comm + +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 + grep, sed, cut, xargs(previous), + style-guide, new-applet-HOWTO, bug fixes, etc. + +Charles P. Wright + gzip, mini-netcat(nc) + +Enrique Zanardi + tarcat (since removed), loadkmap, various fixes, Debian maintenance + +Tito Ragusa + devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm, + fdformat, lsattr, chattr, id and eject. + +Paul Fox + vi editing mode for ash, various other patches/fixes + +Roberto A. Foglietta + port: dnsd + +Bernhard Reutner-Fischer + misc + +Mike Frysinger + initial e2fsprogs, printenv, setarch, sum, misc + +Jie Zhang + fixed two bugs in msh and hush (exitcode of killed processes) + +Maxime Coste + paste implementation diff --git a/busybox/INSTALL b/busybox/INSTALL new file mode 100644 index 00000000000000..750cfc45bfd6de --- /dev/null +++ b/busybox/INSTALL @@ -0,0 +1,142 @@ +Building: +========= + +The BusyBox build process is similar to the Linux kernel build: + + make menuconfig # This creates a file called ".config" + make # This creates the "busybox" executable + make install # or make CONFIG_PREFIX=/path/from/root install + +The full list of configuration and install options is available by typing: + + make help + +Quick Start: +============ + +The easy way to try out BusyBox for the first time, without having to install +it, is to enable all features and then use "standalone shell" mode with a +blank command $PATH. + +To enable all features, use "make defconfig", which produces the largest +general-purpose configuration. It's allyesconfig minus debugging options, +optional packaging choices, and a few special-purpose features requiring +extra configuration to use. Then enable "standalone shell" feature: + + make defconfig + make menuconfig + # select Busybox Settings + # then General Configuration + # then exec prefers applets + # exit back to top level menu + # select Shells + # then Standalone shell + # exit back to top level menu + # exit and save new configuration + # OR + # use these commands to modify .config directly: + sed -e 's/.*FEATURE_PREFER_APPLETS.*/CONFIG_FEATURE_PREFER_APPLETS=y/' -i .config + sed -e 's/.*FEATURE_SH_STANDALONE.*/CONFIG_FEATURE_SH_STANDALONE=y/' -i .config + make + PATH= ./busybox ash + +Standalone shell mode causes busybox's built-in command shell to run +any built-in busybox applets directly, without looking for external +programs by that name. Supplying an empty command path (as above) means +the only commands busybox can find are the built-in ones. + +Note that the standalone shell requires CONFIG_BUSYBOX_EXEC_PATH +to be set appropriately, depending on whether or not /proc/self/exe is +available. If you do not have /proc, then point that config option +to the location of your busybox binary, usually /bin/busybox. +Another solution is to patch the kernel (see +examples/linux-*_proc_self_exe.patch) to make exec("/proc/self/exe") +always work. + +Configuring Busybox: +==================== + +Busybox is optimized for size, but enabling the full set of functionality +still results in a fairly large executable -- more than 1 megabyte when +statically linked. To save space, busybox can be configured with only the +set of applets needed for each environment. The minimal configuration, with +all applets disabled, produces a 4k executable. (It's useless, but very small.) + +The manual configurator "make menuconfig" modifies the existing configuration. +(For systems without ncurses, try "make config" instead.) The two most +interesting starting configurations are "make allnoconfig" (to start with +everything disabled and add just what you need), and "make defconfig" (to +start with everything enabled and remove what you don't need). If menuconfig +is run without an existing configuration, make defconfig will run first to +create a known starting point. + +Other starting configurations (mostly used for testing purposes) include +"make allbareconfig" (enables all applets but disables all optional features), +"make allyesconfig" (enables absolutely everything including debug features), +and "make randconfig" (produce a random configuration). The configs/ directory +contains a number of additional configuration files ending in _defconfig which +are useful in specific cases. "make help" will list them. + +Configuring BusyBox produces a file ".config", which can be saved for future +use. Run "make oldconfig" to bring a .config file from an older version of +busybox up to date. + +Installing Busybox: +=================== + +Busybox is a single executable that can behave like many different commands, +and BusyBox uses the name it was invoked under to determine the desired +behavior. (Try "mv busybox ls" and then "./ls -l".) + +Installing busybox consists of creating symlinks (or hardlinks) to the busybox +binary for each applet enabled in busybox, and making sure these symlinks are +in the shell's command $PATH. Running "make install" creates these symlinks, +or "make install-hardlinks" creates hardlinks instead (useful on systems with +a limited number of inodes). This install process uses the file +"busybox.links" (created by make), which contains the list of enabled applets +and the path at which to install them. + +Installing links to busybox is not always necessary. The special applet name +"busybox" (or with any optional suffix, such as "busybox-static") uses the +first argument to determine which applet to behave as, for example +"./busybox cat LICENSE". (Running the busybox applet with no arguments gives +a list of all enabled applets.) The standalone shell can also call busybox +applets without links to busybox under other names in the filesystem. You can +also configure a standalone install capability into the busybox base applet, +and then install such links at runtime with one of "busybox --install" (for +hardlinks) or "busybox --install -s" (for symlinks). + +If you enabled the busybox shared library feature (libbusybox.so) and want +to run tests without installing, set your LD_LIBRARY_PATH accordingly when +running the executable: + + LD_LIBRARY_PATH=`pwd` ./busybox + +Building out-of-tree: +===================== + +By default, the BusyBox build puts its temporary files in the source tree. +Building from a read-only source tree, or building multiple configurations from +the same source directory, requires the ability to put the temporary files +somewhere else. + +To build out of tree, cd to an empty directory and configure busybox from there: + + make KBUILD_SRC=/path/to/source -f /path/to/source/Makefile defconfig + make + make install + +Alternately, use the O=$BUILDPATH option (with an absolute path) during the +configuration step, as in: + + make O=/some/empty/directory allyesconfig + cd /some/empty/directory + make + make CONFIG_PREFIX=. install + +More Information: +================= + +Se also the busybox FAQ, under the questions "How can I get started using +BusyBox" and "How do I build a BusyBox-based system?" The BusyBox FAQ is +available from http://www.busybox.net/FAQ.html diff --git a/busybox/LICENSE b/busybox/LICENSE new file mode 100644 index 00000000000000..6f50a7164838c3 --- /dev/null +++ b/busybox/LICENSE @@ -0,0 +1,348 @@ +--- A note on GPL versions + +BusyBox is distributed under version 2 of the General Public License (included +in its entirety, below). Version 2 is the only version of this license which +this version of BusyBox (or modified versions derived from this one) may be +distributed under. + +------------------------------------------------------------------------ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/busybox/Makefile b/busybox/Makefile new file mode 100644 index 00000000000000..c5ccc742c551df --- /dev/null +++ b/busybox/Makefile @@ -0,0 +1,1336 @@ +VERSION = 1 +PATCHLEVEL = 30 +SUBLEVEL = 0 +EXTRAVERSION = .git +NAME = Unnamed + +# *DOCUMENTATION* +# To see a list of typical targets execute "make help" +# More info can be located in ./README +# Comments in this file are targeted only to the developer, do not +# expect to learn how to build the kernel reading this file. + +# Do not print "Entering directory ..." +MAKEFLAGS += --no-print-directory + +# We are using a recursive build, so we need to do a little thinking +# to get the ordering right. +# +# Most importantly: sub-Makefiles should only ever modify files in +# their own directory. If in some directory we have a dependency on +# a file in another dir (which doesn't happen often, but it's often +# unavoidable when linking the built-in.o targets which finally +# turn into busybox), we will call a sub make in that other dir, and +# after that we are sure that everything which is in that other dir +# is now up to date. +# +# The only cases where we need to modify files which have global +# effects are thus separated out and done before the recursive +# descending is started. They are now explicitly listed as the +# prepare rule. + +# To put more focus on warnings, be less verbose as default +# Use 'make V=1' to see the full commands + +ifdef V + ifeq ("$(origin V)", "command line") + KBUILD_VERBOSE = $(V) + endif +endif +ifndef KBUILD_VERBOSE + KBUILD_VERBOSE = 0 +endif + +# Call sparse as part of compilation of C files +# Use 'make C=1' to enable sparse checking + +ifdef C + ifeq ("$(origin C)", "command line") + KBUILD_CHECKSRC = $(C) + endif +endif +ifndef KBUILD_CHECKSRC + KBUILD_CHECKSRC = 0 +endif + +# Use make M=dir to specify directory of external module to build +# Old syntax make ... SUBDIRS=$PWD is still supported +# Setting the environment variable KBUILD_EXTMOD take precedence +ifdef SUBDIRS + KBUILD_EXTMOD ?= $(SUBDIRS) +endif +ifdef M + ifeq ("$(origin M)", "command line") + KBUILD_EXTMOD := $(M) + endif +endif + + +# kbuild supports saving output files in a separate directory. +# To locate output files in a separate directory two syntaxes are supported. +# In both cases the working directory must be the root of the kernel src. +# 1) O= +# Use "make O=dir/to/store/output/files/" +# +# 2) Set KBUILD_OUTPUT +# Set the environment variable KBUILD_OUTPUT to point to the directory +# where the output files shall be placed. +# export KBUILD_OUTPUT=dir/to/store/output/files/ +# make +# +# The O= assignment takes precedence over the KBUILD_OUTPUT environment +# variable. + + +# KBUILD_SRC is set on invocation of make in OBJ directory +# KBUILD_SRC is not intended to be used by the regular user (for now) +ifeq ($(KBUILD_SRC),) + +# OK, Make called in directory where kernel src resides +# Do we want to locate output files in a separate directory? +ifdef O + ifeq ("$(origin O)", "command line") + KBUILD_OUTPUT := $(O) + endif +endif + +# That's our default target when none is given on the command line +PHONY := _all +_all: + +ifneq ($(KBUILD_OUTPUT),) +# Invoke a second make in the output directory, passing relevant variables +# check that the output directory actually exists +saved-output := $(KBUILD_OUTPUT) +KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd) +$(if $(KBUILD_OUTPUT),, \ + $(error output directory "$(saved-output)" does not exist)) + +PHONY += $(MAKECMDGOALS) + +$(filter-out _all,$(MAKECMDGOALS)) _all: + $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \ + KBUILD_SRC=$(CURDIR) \ + KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile $@ + +# Leave processing to above invocation of make +skip-makefile := 1 +endif # ifneq ($(KBUILD_OUTPUT),) +endif # ifeq ($(KBUILD_SRC),) + +# We process the rest of the Makefile if this is the final invocation of make +ifeq ($(skip-makefile),) + +# If building an external module we do not care about the all: rule +# but instead _all depend on modules +PHONY += all +ifeq ($(KBUILD_EXTMOD),) +_all: all +else +_all: modules +endif + +srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR)) +TOPDIR := $(srctree) +# FIXME - TOPDIR is obsolete, use srctree/objtree +objtree := $(CURDIR) +src := $(srctree) +obj := $(objtree) + +VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD)) + +export srctree objtree VPATH TOPDIR + + +# Cross compiling and selecting different set of gcc/bin-utils +# --------------------------------------------------------------------------- +# +# When performing cross compilation for other architectures ARCH shall be set +# to the target architecture. (See arch/* for the possibilities). +# ARCH can be set during invocation of make: +# make ARCH=ia64 +# Another way is to have ARCH set in the environment. +# The default ARCH is the host where make is executed. + +# CROSS_COMPILE specify the prefix used for all executables used +# during compilation. Only gcc and related bin-utils executables +# are prefixed with $(CROSS_COMPILE). +# CROSS_COMPILE can be set on the command line +# make CROSS_COMPILE=ia64-linux- +# Alternatively CROSS_COMPILE can be set in the environment. +# Default value for CROSS_COMPILE is not to prefix executables +# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile + +CROSS_COMPILE ?= +# bbox: we may have CONFIG_CROSS_COMPILER_PREFIX in .config, +# and it has not been included yet... thus using an awkward syntax. +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := $(shell grep ^CONFIG_CROSS_COMPILER_PREFIX .config 2>/dev/null) +CROSS_COMPILE := $(subst CONFIG_CROSS_COMPILER_PREFIX=,,$(CROSS_COMPILE)) +CROSS_COMPILE := $(subst ",,$(CROSS_COMPILE)) +#") +endif + +# SUBARCH tells the usermode build what the underlying arch is. That is set +# first, and if a usermode build is happening, the "ARCH=um" on the command +# line overrides the setting of ARCH below. If a native build is happening, +# then ARCH is assigned, getting whatever value it gets normally, and +# SUBARCH is subsequently ignored. + +ifneq ($(CROSS_COMPILE),) +SUBARCH := $(shell echo $(CROSS_COMPILE) | cut -d- -f1 | sed 's:^.*/::g') +else +SUBARCH := $(shell uname -m) +endif +SUBARCH := $(shell echo $(SUBARCH) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ + -e s/arm.*/arm/ -e s/sa110/arm/ \ + -e s/s390x/s390/ -e s/parisc64/parisc/ \ + -e s/ppc.*/powerpc/ -e s/mips.*/mips/ ) + +ARCH ?= $(SUBARCH) + +# Architecture as present in compile.h +UTS_MACHINE := $(ARCH) + +# SHELL used by kbuild +CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ + else if [ -x /bin/bash ]; then echo /bin/bash; \ + else echo sh; fi ; fi) + +# Decide whether to build built-in, modular, or both. +# Normally, just do built-in. + +KBUILD_MODULES := +KBUILD_BUILTIN := 1 + +# If we have only "make modules", don't compile built-in objects. +# When we're building modules with modversions, we need to consider +# the built-in objects during the descend as well, in order to +# make sure the checksums are uptodate before we record them. + +ifeq ($(MAKECMDGOALS),modules) + KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1) +endif + +# If we have "make modules", compile modules +# in addition to whatever we do anyway. +# Just "make" or "make all" shall build modules as well + +ifneq ($(filter all _all modules,$(MAKECMDGOALS)),) + KBUILD_MODULES := 1 +endif + +ifeq ($(MAKECMDGOALS),) + KBUILD_MODULES := 1 +endif + +export KBUILD_MODULES KBUILD_BUILTIN +export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD + +# Beautify output +# --------------------------------------------------------------------------- +# +# Normally, we echo the whole command before executing it. By making +# that echo $($(quiet)$(cmd)), we now have the possibility to set +# $(quiet) to choose other forms of output instead, e.g. +# +# quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@ +# cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< +# +# If $(quiet) is empty, the whole command will be printed. +# If it is set to "quiet_", only the short version will be printed. +# If it is set to "silent_", nothing wil be printed at all, since +# the variable $(silent_cmd_cc_o_c) doesn't exist. +# +# A simple variant is to prefix commands with $(Q) - that's useful +# for commands that shall be hidden in non-verbose mode. +# +# $(Q)ln $@ :< +# +# If KBUILD_VERBOSE equals 0 then the above command will be hidden. +# If KBUILD_VERBOSE equals 1 then the above command is displayed. + +ifeq ($(KBUILD_VERBOSE),1) + quiet = + Q = +else + quiet=quiet_ + Q = @ +endif + +# If the user is running make -s (silent mode), suppress echoing of +# commands + +ifneq ($(findstring s,$(MAKEFLAGS)),) + quiet=silent_ +endif + +export quiet Q KBUILD_VERBOSE + + +# Look for make include files relative to root of kernel src +MAKEFLAGS += --include-dir=$(srctree) + +HOSTCC = gcc +HOSTCXX = g++ +HOSTCFLAGS := +HOSTCXXFLAGS := +# We need some generic definitions +include $(srctree)/scripts/Kbuild.include + +HOSTCFLAGS += $(call hostcc-option,-Wall -Wstrict-prototypes -O2 -fomit-frame-pointer,) +HOSTCXXFLAGS += -O2 + +# For maximum performance (+ possibly random breakage, uncomment +# the following) + +MAKEFLAGS += -rR + +# Make variables (CC, etc...) + +AS = $(CROSS_COMPILE)as +CC = $(CROSS_COMPILE)gcc +LD = $(CC) -nostdlib +CPP = $(CC) -E +AR = $(CROSS_COMPILE)ar +NM = $(CROSS_COMPILE)nm +STRIP = $(CROSS_COMPILE)strip +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJDUMP = $(CROSS_COMPILE)objdump +PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config +AWK = awk +GENKSYMS = scripts/genksyms/genksyms +DEPMOD = /sbin/depmod +KALLSYMS = scripts/kallsyms +PERL = perl +CHECK = sparse + +CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise $(CF) +MODFLAGS = -DMODULE +CFLAGS_MODULE = $(MODFLAGS) +AFLAGS_MODULE = $(MODFLAGS) +LDFLAGS_MODULE = -r +CFLAGS_KERNEL = +AFLAGS_KERNEL = + + +# Use LINUXINCLUDE when you must reference the include/ directory. +# Needed to be compatible with the O= option +CFLAGS := $(CFLAGS) +# Added only to final link stage of busybox binary +CFLAGS_busybox := $(CFLAGS_busybox) +CPPFLAGS := $(CPPFLAGS) +AFLAGS := $(AFLAGS) +LDFLAGS := $(LDFLAGS) +LDLIBS := + +# Read KERNELRELEASE from .kernelrelease (if it exists) +KERNELRELEASE = $(shell cat .kernelrelease 2> /dev/null) +KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) + +export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION \ + ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \ + CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE \ + HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS + +export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS +export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE +export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE +export FLTFLAGS + +# When compiling out-of-tree modules, put MODVERDIR in the module +# tree rather than in the kernel tree. The kernel tree might +# even be read-only. +export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions + +# Files to ignore in find ... statements + +RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git \) -prune -o +export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS --exclude .pc --exclude .hg --exclude .git + +# =========================================================================== +# Rules shared between *config targets and build targets + +# Basic helpers built in scripts/ +PHONY += scripts_basic +scripts_basic: + $(Q)$(MAKE) $(build)=scripts/basic + +# To avoid any implicit rule to kick in, define an empty command. +scripts/basic/%: scripts_basic ; + +# This target generates Kbuild's and Config.in's from *.c files +PHONY += gen_build_files +gen_build_files: $(wildcard $(srctree)/*/*.c) $(wildcard $(srctree)/*/*/*.c) + $(Q)$(srctree)/scripts/gen_build_files.sh $(srctree) $(objtree) + +# bbox: we have helpers in applets/ +# we depend on scripts_basic, since scripts/basic/fixdep +# must be built before any other host prog +PHONY += applets_dir +applets_dir: scripts_basic gen_build_files include/config/MARKER + $(Q)$(MAKE) $(build)=applets + +applets/%: applets_dir ; + +PHONY += outputmakefile +# outputmakefile generates a Makefile in the output directory, if using a +# separate output directory. This allows convenient use of make in the +# output directory. +outputmakefile: +ifneq ($(KBUILD_SRC),) + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \ + $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) +endif + +# To make sure we do not include .config for any of the *config targets +# catch them early, and hand them over to scripts/kconfig/Makefile +# It is allowed to specify more targets when calling make, including +# mixing *config targets and build targets. +# For example 'make oldconfig all'. +# Detect when mixed targets is specified, and make a second invocation +# of make so .config is not included in this case either (for *config). + +no-dot-config-targets := clean mrproper distclean \ + cscope TAGS tags help %docs +#bbox# check% is removed from above + +config-targets := 0 +mixed-targets := 0 +dot-config := 1 + +ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),) + ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),) + dot-config := 0 + endif +endif + +ifeq ($(KBUILD_EXTMOD),) + ifneq ($(filter config %config,$(MAKECMDGOALS)),) + config-targets := 1 + ifneq ($(filter-out config %config,$(MAKECMDGOALS)),) + mixed-targets := 1 + endif + endif +endif + +ifeq ($(mixed-targets),1) +# =========================================================================== +# We're called with mixed targets (*config and build targets). +# Handle them one by one. + +%:: FORCE + $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@ + +else +ifeq ($(config-targets),1) +# =========================================================================== +# *config targets only - make sure prerequisites are updated, and descend +# in scripts/kconfig to make the *config target + +# Read arch specific Makefile to set KBUILD_DEFCONFIG as needed. +# KBUILD_DEFCONFIG may point out an alternative default configuration +# used for 'make defconfig' +-include $(srctree)/arch/$(ARCH)/Makefile +export KBUILD_DEFCONFIG + +config: scripts_basic outputmakefile gen_build_files FORCE + $(Q)mkdir -p include + $(Q)$(MAKE) $(build)=scripts/kconfig $@ + $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease + +%config: scripts_basic outputmakefile gen_build_files FORCE + $(Q)mkdir -p include + $(Q)$(MAKE) $(build)=scripts/kconfig $@ + $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= .kernelrelease + +else +# =========================================================================== +# Build targets only - this includes busybox, arch specific targets, clean +# targets and others. In general all targets except *config targets. + +ifeq ($(KBUILD_EXTMOD),) +# Additional helpers built in scripts/ +# Carefully list dependencies so we do not try to build scripts twice +# in parallel +PHONY += scripts +scripts: gen_build_files scripts_basic include/config/MARKER + $(Q)$(MAKE) $(build)=$(@) + +scripts_basic: include/autoconf.h + +# Objects we will link into busybox / subdirs we need to visit +core-y := \ + applets/ \ + +libs-y := \ + archival/ \ + archival/libarchive/ \ + console-tools/ \ + coreutils/ \ + coreutils/libcoreutils/ \ + debianutils/ \ + klibc-utils/ \ + e2fsprogs/ \ + editors/ \ + findutils/ \ + init/ \ + libbb/ \ + libpwdgrp/ \ + loginutils/ \ + mailutils/ \ + miscutils/ \ + modutils/ \ + networking/ \ + networking/libiproute/ \ + networking/udhcp/ \ + printutils/ \ + procps/ \ + runit/ \ + selinux/ \ + shell/ \ + sysklogd/ \ + util-linux/ \ + util-linux/volume_id/ \ + +endif # KBUILD_EXTMOD + +ifeq ($(dot-config),1) +# In this section, we need .config + +# Read in dependencies to all Kconfig* files, make sure to run +# oldconfig if changes are detected. +-include .kconfig.d + +-include .config + +# If .config needs to be updated, it will be done via the dependency +# that autoconf has on .config. +# To avoid any implicit rule to kick in, define an empty command +.config .kconfig.d: ; + +-include $(srctree)/arch/$(ARCH)/Makefile + +# Now we can define CFLAGS etc according to .config +include $(srctree)/Makefile.flags + +# If .config is newer than include/autoconf.h, someone tinkered +# with it and forgot to run make oldconfig. +# If kconfig.d is missing then we are probarly in a cleaned tree so +# we execute the config step to be sure to catch updated Kconfig files +include/autoconf.h: .kconfig.d .config $(wildcard $(srctree)/*/*.c) $(wildcard $(srctree)/*/*/*.c) | gen_build_files + $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig + +include/usage.h: gen_build_files + +else +# Dummy target needed, because used as prerequisite +include/autoconf.h: ; +endif + +# The all: target is the default when no target is given on the +# command line. +# This allow a user to issue only 'make' to build a kernel including modules +# Defaults busybox but it is usually overridden in the arch makefile +all: busybox doc + +# arch Makefile may override CC so keep this after arch Makefile is included +#bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) +CHECKFLAGS += $(NOSTDINC_FLAGS) + +# Default kernel image to build when no specific target is given. +# KBUILD_IMAGE may be overruled on the commandline or +# set in the environment +# Also any assignments in arch/$(ARCH)/Makefile take precedence over +# this default value +export KBUILD_IMAGE ?= busybox + +# +# INSTALL_PATH specifies where to place the updated kernel and system map +# images. Default is /boot, but you can set it to other values +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 argument can be passed to make if needed. +# + +MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE) +export MODLIB + + +ifeq ($(KBUILD_EXTMOD),) +busybox-dirs := $(patsubst %/,%,$(filter %/, $(core-y) $(core-m) $(libs-y) $(libs-m))) + +busybox-alldirs := $(sort $(busybox-dirs) $(patsubst %/,%,$(filter %/, \ + $(core-n) $(core-) $(libs-n) $(libs-) \ + ))) + +core-y := $(patsubst %/, %/built-in.o, $(core-y)) +libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) +libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) +libs-y := $(libs-y1) $(libs-y2) + +# Build busybox +# --------------------------------------------------------------------------- +# busybox is build from the objects selected by $(busybox-init) and +# $(busybox-main). Most are built-in.o files from top-level directories +# in the kernel tree, others are specified in arch/$(ARCH)Makefile. +# Ordering when linking is important, and $(busybox-init) must be first. +# +# busybox +# ^ +# | +# +-< $(busybox-init) +# | +--< init/version.o + more +# | +# +--< $(busybox-main) +# | +--< driver/built-in.o mm/built-in.o + more +# | +# +-< kallsyms.o (see description in CONFIG_KALLSYMS section) +# +# busybox version (uname -v) cannot be updated during normal +# descending-into-subdirs phase since we do not yet know if we need to +# update busybox. +# Therefore this step is delayed until just before final link of busybox - +# except in the kallsyms case where it is done just before adding the +# symbols to the kernel. +# +# System.map is generated to document addresses of all kernel symbols + +busybox-all := $(core-y) $(libs-y) + +# Rule to link busybox - also used during CONFIG_KALLSYMS +# May be overridden by arch/$(ARCH)/Makefile +quiet_cmd_busybox__ ?= LINK $@ + cmd_busybox__ ?= $(srctree)/scripts/trylink \ + "$@" \ + "$(CC)" \ + "$(CFLAGS) $(CFLAGS_busybox)" \ + "$(LDFLAGS) $(EXTRA_LDFLAGS)" \ + "$(core-y)" \ + "$(libs-y)" \ + "$(LDLIBS)" \ + && $(srctree)/scripts/generate_BUFSIZ.sh --post include/common_bufsiz.h + +# Generate System.map +quiet_cmd_sysmap = SYSMAP + cmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap + +# Link of busybox +# If CONFIG_KALLSYMS is set .version is already updated +# Generate System.map and verify that the content is consistent +# Use + in front of the busybox_version rule to silent warning with make -j2 +# First command is ':' to allow us to use + in front of the rule +define rule_busybox__ + : + $(call cmd,busybox__) + $(Q)echo 'cmd_$@ := $(cmd_busybox__)' > $(@D)/.$(@F).cmd +endef + + +ifdef CONFIG_KALLSYMS +# Generate section listing all symbols and add it into busybox $(kallsyms.o) +# It's a three stage process: +# o .tmp_busybox1 has all symbols and sections, but __kallsyms is +# empty +# Running kallsyms on that gives us .tmp_kallsyms1.o with +# the right size - busybox version (uname -v) is updated during this step +# o .tmp_busybox2 now has a __kallsyms section of the right size, +# but due to the added section, some addresses have shifted. +# From here, we generate a correct .tmp_kallsyms2.o +# o The correct .tmp_kallsyms2.o is linked into the final busybox. +# o Verify that the System.map from busybox matches the map from +# .tmp_busybox2, just in case we did not generate kallsyms correctly. +# o If CONFIG_KALLSYMS_EXTRA_PASS is set, do an extra pass using +# .tmp_busybox3 and .tmp_kallsyms3.o. This is only meant as a +# temporary bypass to allow the kernel to be built while the +# maintainers work out what went wrong with kallsyms. + +ifdef CONFIG_KALLSYMS_EXTRA_PASS +last_kallsyms := 3 +else +last_kallsyms := 2 +endif + +kallsyms.o := .tmp_kallsyms$(last_kallsyms).o + +define verify_kallsyms + $(Q)$(if $($(quiet)cmd_sysmap), \ + echo ' $($(quiet)cmd_sysmap) .tmp_System.map' &&) \ + $(cmd_sysmap) .tmp_busybox$(last_kallsyms) .tmp_System.map + $(Q)cmp -s System.map .tmp_System.map || \ + (echo Inconsistent kallsyms data; \ + echo Try setting CONFIG_KALLSYMS_EXTRA_PASS; \ + rm .tmp_kallsyms* ; /bin/false ) +endef + +# Update busybox version before link +# Use + in front of this rule to silent warning about make -j1 +# First command is ':' to allow us to use + in front of this rule +cmd_ksym_ld = $(cmd_busybox__) +define rule_ksym_ld + : + +$(call cmd,busybox_version) + $(call cmd,busybox__) + $(Q)echo 'cmd_$@ := $(cmd_busybox__)' > $(@D)/.$(@F).cmd +endef + +# Generate .S file with all kernel symbols +quiet_cmd_kallsyms = KSYM $@ + cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \ + $(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > $@ + +.tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE + $(call if_changed_dep,as_o_S) + +.tmp_kallsyms%.S: .tmp_busybox% $(KALLSYMS) + $(call cmd,kallsyms) + +# .tmp_busybox1 must be complete except kallsyms, so update busybox version +.tmp_busybox1: $(busybox-lds) $(busybox-all) FORCE + $(call if_changed_rule,ksym_ld) + +.tmp_busybox2: $(busybox-lds) $(busybox-all) .tmp_kallsyms1.o FORCE + $(call if_changed,busybox__) + +.tmp_busybox3: $(busybox-lds) $(busybox-all) .tmp_kallsyms2.o FORCE + $(call if_changed,busybox__) + +# Needs to visit scripts/ before $(KALLSYMS) can be used. +$(KALLSYMS): scripts ; + +# Generate some data for debugging strange kallsyms problems +debug_kallsyms: .tmp_map$(last_kallsyms) + +.tmp_map%: .tmp_busybox% FORCE + ($(OBJDUMP) -h $< | $(AWK) '/^ +[0-9]/{print $$4 " 0 " $$2}'; $(NM) $<) | sort > $@ + +.tmp_map3: .tmp_map2 + +.tmp_map2: .tmp_map1 + +endif # ifdef CONFIG_KALLSYMS + +# busybox image - including updated kernel symbols +busybox_unstripped: $(busybox-all) FORCE + $(call if_changed_rule,busybox__) + $(Q)rm -f .old_version + +busybox: busybox_unstripped +ifeq ($(SKIP_STRIP),y) + $(Q)cp $< $@ +else + $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \ + busybox_unstripped -o $@ +# strip is confused by PIE executable and does not set exec bits + $(Q)chmod a+x $@ +endif + +# The actual objects are generated when descending, +# make sure no implicit rule kicks in +$(sort $(busybox-all)): $(busybox-dirs) ; + +# Handle descending into subdirectories listed in $(busybox-dirs) +# Preset locale variables to speed up the build process. Limit locale +# tweaks to this spot to avoid wrong language settings when running +# make menuconfig etc. +# Error messages still appears in the original language + +PHONY += $(busybox-dirs) +$(busybox-dirs): prepare scripts + $(Q)$(MAKE) $(build)=$@ + +# Build the kernel release string +# The KERNELRELEASE is stored in a file named .kernelrelease +# to be used when executing for example make install or make modules_install +# +# Take the contents of any files called localversion* and the config +# variable CONFIG_LOCALVERSION and append them to KERNELRELEASE. +# LOCALVERSION from the command line override all of this + +nullstring := +space := $(nullstring) # end of line + +___localver = $(objtree)/localversion* $(srctree)/localversion* +__localver = $(sort $(wildcard $(___localver))) +# skip backup files (containing '~') +_localver = $(foreach f, $(__localver), $(if $(findstring ~, $(f)),,$(f))) + +localver = $(subst $(space),, \ + $(shell cat /dev/null $(_localver)) \ + $(patsubst "%",%,$(CONFIG_LOCALVERSION))) + +# If CONFIG_LOCALVERSION_AUTO is set scripts/setlocalversion is called +# and if the SCM is know a tag from the SCM is appended. +# The appended tag is determinded by the SCM used. +# +# Currently, only git is supported. +# Other SCMs can edit scripts/setlocalversion and add the appropriate +# checks as needed. +ifdef _BB_DISABLED_CONFIG_LOCALVERSION_AUTO + _localver-auto = $(shell $(CONFIG_SHELL) \ + $(srctree)/scripts/setlocalversion $(srctree)) + localver-auto = $(LOCALVERSION)$(_localver-auto) +endif + +localver-full = $(localver)$(localver-auto) + +# Store (new) KERNELRELASE string in .kernelrelease +kernelrelease = $(KERNELVERSION)$(localver-full) +.kernelrelease: FORCE + $(Q)rm -f $@ + $(Q)echo $(kernelrelease) > $@ + + +# Things we need to do before we recursively start building the kernel +# or the modules are listed in "prepare". +# A multi level approach is used. prepareN is processed before prepareN-1. +# archprepare is used in arch Makefiles and when processed asm symlink, +# version.h and scripts_basic is processed / created. + +# Listed in dependency order +PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3 + +# prepare-all is deprecated, use prepare as valid replacement +PHONY += prepare-all + +# prepare3 is used to check if we are building in a separate output directory, +# and if so do: +# 1) Check that make has not been executed in the kernel src $(srctree) +# 2) Create the include2 directory, used for the second asm symlink +prepare3: .kernelrelease +ifneq ($(KBUILD_SRC),) + @echo ' Using $(srctree) as source for busybox' + $(Q)if [ -f $(srctree)/.config ]; then \ + echo " $(srctree) is not clean, please run 'make mrproper'";\ + echo " in the '$(srctree)' directory.";\ + /bin/false; \ + fi; + $(Q)if [ ! -d include2 ]; then mkdir -p include2; fi; + $(Q)ln -fsn $(srctree)/include/asm-$(ARCH) include2/asm +endif + +# prepare2 creates a makefile if using a separate output directory +prepare2: prepare3 outputmakefile + +prepare1: prepare2 include/config/MARKER +ifneq ($(KBUILD_MODULES),) + $(Q)mkdir -p $(MODVERDIR) + $(Q)rm -f $(MODVERDIR)/* +endif + +archprepare: prepare1 scripts_basic applets_dir + +prepare0: archprepare FORCE + $(Q)$(MAKE) $(build)=. + +# All the preparing.. +prepare prepare-all: prepare0 + +# Leave this as default for preprocessing busybox.lds.S, which is now +# done in arch/$(ARCH)/kernel/Makefile + +export CPPFLAGS_busybox.lds += -P -C -U$(ARCH) + +# FIXME: The asm symlink changes when $(ARCH) changes. That's +# hard to detect, but I suppose "make mrproper" is a good idea +# before switching between archs anyway. + +#bbox# include/asm: +#bbox# @echo ' SYMLINK $@ -> include/asm-$(ARCH)' +#bbox# $(Q)if [ ! -d include ]; then mkdir -p include; fi; +#bbox# @ln -fsn asm-$(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 +# --------------------------------------------------------------------------- + +# KERNELRELEASE can change from a few different places, meaning version.h +# needs to be updated, so this check is forced on all builds + +uts_len := 64 + +define filechk_version.h + if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \ + echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \ + exit 1; \ + fi; \ + (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\"; \ + echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)`; \ + echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))'; \ + ) +endef + +# --------------------------------------------------------------------------- + +PHONY += depend dep +depend dep: + @echo '*** Warning: make $@ is unnecessary now.' + +# --------------------------------------------------------------------------- +# Modules + +ifdef _BB_DISABLED_CONFIG_MODULES + +# By default, build modules as well + +all: modules + +# Build modules + +PHONY += modules +modules: $(busybox-dirs) $(if $(KBUILD_BUILTIN),busybox) + @echo ' Building modules, stage 2.'; + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost + + +# Target to prepare building external modules +PHONY += modules_prepare +modules_prepare: prepare scripts + +# Target to install modules +PHONY += modules_install +modules_install: _modinst_ _modinst_post + +PHONY += _modinst_ +_modinst_: + @if [ -z "`$(DEPMOD) -V 2>/dev/null | grep module-init-tools`" ]; then \ + echo "Warning: you may need to install module-init-tools"; \ + echo "See http://www.codemonkey.org.uk/docs/post-halloween-2.6.txt";\ + sleep 1; \ + fi + @rm -rf $(MODLIB)/kernel + @rm -f $(MODLIB)/source + @mkdir -p $(MODLIB)/kernel + @ln -s $(srctree) $(MODLIB)/source + @if [ ! $(objtree) -ef $(MODLIB)/build ]; then \ + rm -f $(MODLIB)/build ; \ + ln -s $(objtree) $(MODLIB)/build ; \ + fi + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst + +# If System.map exists, run depmod. This deliberately does not have a +# dependency on System.map since that would run the dependency tree on +# busybox. This depmod is only for convenience to give the initial +# boot a modules.dep even before / is mounted read-write. However the +# boot script depmod is the master version. +ifeq "$(strip $(INSTALL_MOD_PATH))" "" +depmod_opts := +else +depmod_opts := -b $(INSTALL_MOD_PATH) -r +endif +PHONY += _modinst_post +_modinst_post: _modinst_ + if [ -r System.map -a -x $(DEPMOD) ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi + +else # CONFIG_MODULES + +# Modules not configured +# --------------------------------------------------------------------------- + +modules modules_install: FORCE + @echo + @echo "The present busybox configuration has modules disabled." + @echo "Type 'make config' and enable loadable module support." + @echo "Then build a kernel with module support enabled." + @echo + @exit 1 + +endif # CONFIG_MODULES + +### +# Cleaning is done on three levels. +# make clean Delete most generated files +# Leave enough to build external modules +# make mrproper Delete the current configuration, and all generated files +# make distclean Remove editor backup files, patch leftover files and the like + +# Directories & files removed with 'make clean' +CLEAN_DIRS += $(MODVERDIR) _install 0_lib +CLEAN_FILES += busybox busybox_unstripped* busybox.links \ + System.map .kernelrelease \ + .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map + +# Directories & files removed with 'make mrproper' +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 \ + include/usage_compressed.h \ + include/applet_tables.h \ + include/applets.h \ + include/usage.h \ + applets/usage \ + .kernelrelease Module.symvers tags TAGS cscope* \ + busybox_old + +# clean - Delete most, but leave enough to build external modules +# +clean: rm-dirs := $(CLEAN_DIRS) +clean: rm-files := $(CLEAN_FILES) +clean-dirs := $(addprefix _clean_,$(srctree) $(busybox-alldirs)) + +PHONY += $(clean-dirs) clean archclean +$(clean-dirs): + $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) + +clean: archclean $(clean-dirs) + $(call cmd,rmdirs) + $(call cmd,rmfiles) + @find . $(RCS_FIND_IGNORE) \ + \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ + -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \ + -type f -print | xargs rm -f + +PHONY += doc-clean +doc-clean: rm-files := docs/busybox.pod \ + docs/BusyBox.html docs/busybox.1 docs/BusyBox.txt +doc-clean: + $(call cmd,rmfiles) + +# mrproper - Delete all generated files, including .config +# +mrproper: rm-dirs := $(wildcard $(MRPROPER_DIRS)) +mrproper: rm-files := $(wildcard $(MRPROPER_FILES)) +mrproper-dirs := $(addprefix _mrproper_,scripts) + +PHONY += $(mrproper-dirs) mrproper archmrproper +$(mrproper-dirs): + $(Q)$(MAKE) $(clean)=$(patsubst _mrproper_%,%,$@) + +mrproper: clean archmrproper $(mrproper-dirs) + $(call cmd,rmdirs) + $(call cmd,rmfiles) + @find . -name Config.src | sed 's/.src$$/.in/' | xargs -r rm -f + @find . -name Kbuild.src | sed 's/.src$$//' | xargs -r rm -f + +# distclean +# +PHONY += distclean + +distclean: mrproper + @find $(srctree) $(RCS_FIND_IGNORE) \ + \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ + -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ + -o -name '.*.rej' -o -name '*.tmp' -o -size 0 \ + -o -name '*%' -o -name '.*.cmd' -o -name 'core' \) \ + -type f -print | xargs rm -f + + +# Packaging of the kernel to various formats +# --------------------------------------------------------------------------- +# rpm target kept for backward compatibility +package-dir := $(srctree)/scripts/package + +%pkg: FORCE + $(Q)$(MAKE) $(build)=$(package-dir) $@ +rpm: FORCE + $(Q)$(MAKE) $(build)=$(package-dir) $@ + + +# Brief documentation of the typical targets used +# --------------------------------------------------------------------------- + +boards := $(wildcard $(srctree)/configs/*_defconfig) +boards := $(notdir $(boards)) + +-include $(srctree)/Makefile.help + +# Documentation targets +# --------------------------------------------------------------------------- +%docs: scripts_basic FORCE + $(Q)$(MAKE) $(build)=Documentation/DocBook $@ + +else # KBUILD_EXTMOD + +### +# External module support. +# When building external modules the kernel used as basis is considered +# read-only, and no consistency checks are made and the make +# system is not used on the basis kernel. If updates are required +# in the basis kernel ordinary make commands (without M=...) must +# be used. +# +# The following are the only valid targets when building external +# modules. +# make M=dir clean Delete all automatically generated files +# make M=dir modules Make all modules in specified dir +# make M=dir Same as 'make M=dir modules' +# make M=dir modules_install +# Install the modules build in the module directory +# Assumes install directory is already created + +# We are always building modules +KBUILD_MODULES := 1 +PHONY += crmodverdir +crmodverdir: + $(Q)mkdir -p $(MODVERDIR) + $(Q)rm -f $(MODVERDIR)/* + +PHONY += $(objtree)/Module.symvers +$(objtree)/Module.symvers: + @test -e $(objtree)/Module.symvers || ( \ + echo; \ + echo " WARNING: Symbol version dump $(objtree)/Module.symvers"; \ + echo " is missing; modules will have no dependencies and modversions."; \ + echo ) + +module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD)) +PHONY += $(module-dirs) modules +$(module-dirs): crmodverdir $(objtree)/Module.symvers + $(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@) + +modules: $(module-dirs) + @echo ' Building modules, stage 2.'; + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost + +PHONY += modules_install +modules_install: _emodinst_ _emodinst_post + +install-dir := $(if $(INSTALL_MOD_DIR),$(INSTALL_MOD_DIR),extra) +PHONY += _emodinst_ +_emodinst_: + $(Q)mkdir -p $(MODLIB)/$(install-dir) + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst + +# Run depmod only is we have System.map and depmod is executable +quiet_cmd_depmod = DEPMOD $(KERNELRELEASE) + cmd_depmod = if [ -r System.map -a -x $(DEPMOD) ]; then \ + $(DEPMOD) -ae -F System.map \ + $(if $(strip $(INSTALL_MOD_PATH)), \ + -b $(INSTALL_MOD_PATH) -r) \ + $(KERNELRELEASE); \ + fi + +PHONY += _emodinst_post +_emodinst_post: _emodinst_ + $(call cmd,depmod) + +clean-dirs := $(addprefix _clean_,$(KBUILD_EXTMOD)) + +PHONY += $(clean-dirs) clean +$(clean-dirs): + $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) + +clean: rm-dirs := $(MODVERDIR) +clean: $(clean-dirs) + $(call cmd,rmdirs) + @find $(KBUILD_EXTMOD) $(RCS_FIND_IGNORE) \ + \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ + -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \ + -type f -print | xargs rm -f + +# Dummies... +PHONY += prepare scripts +prepare: ; +scripts: ; +endif # KBUILD_EXTMOD + +# Generate tags for editors +# --------------------------------------------------------------------------- + +#We want __srctree to totally vanish out when KBUILD_OUTPUT is not set +#(which is the most common case IMHO) to avoid unneeded clutter in the big tags file. +#Adding $(srctree) adds about 20M on i386 to the size of the output file! + +ifeq ($(src),$(obj)) +__srctree = +else +__srctree = $(srctree)/ +endif + +ifeq ($(ALLSOURCE_ARCHS),) +ifeq ($(ARCH),um) +ALLINCLUDE_ARCHS := $(ARCH) $(SUBARCH) +else +ALLINCLUDE_ARCHS := $(ARCH) +endif +else +#Allow user to specify only ALLSOURCE_PATHS on the command line, keeping existing behaviour. +ALLINCLUDE_ARCHS := $(ALLSOURCE_ARCHS) +endif + +ALLSOURCE_ARCHS := $(ARCH) + +define all-sources + ( find -regex '.*\.[ch]$$' ) +endef + +quiet_cmd_cscope-file = FILELST cscope.files + cmd_cscope-file = (echo \-k; echo \-q; $(all-sources)) > cscope.files + +quiet_cmd_cscope = MAKE cscope.out + cmd_cscope = cscope -b + +cscope: FORCE + $(call cmd,cscope-file) + $(call cmd,cscope) + +quiet_cmd_TAGS = MAKE $@ +define cmd_TAGS + rm -f $@; \ + ETAGSF=`etags --version | grep -i exuberant >/dev/null && \ + echo "-I __initdata,__exitdata,__acquires,__releases \ + -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \ + --extra=+f --c-kinds=+px"`; \ + $(all-sources) | xargs etags $$ETAGSF -a +endef + +TAGS: FORCE + $(call cmd,TAGS) + + +quiet_cmd_tags = MAKE $@ +define cmd_tags + rm -f $@; \ + CTAGSF=`ctags --version | grep -i exuberant >/dev/null && \ + echo "-I __initdata,__exitdata,__acquires,__releases \ + -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \ + --extra=+f --c-kinds=+px"`; \ + $(all-sources) | xargs ctags $$CTAGSF -a +endef + +tags: FORCE + $(call cmd,tags) + + +# Scripts to check various things for consistency +# --------------------------------------------------------------------------- + +includecheck: + find * $(RCS_FIND_IGNORE) \ + -name '*.[hcS]' -type f -print | sort \ + | xargs $(PERL) -w scripts/checkincludes.pl + +versioncheck: + find * $(RCS_FIND_IGNORE) \ + -name '*.[hcS]' -type f -print | sort \ + | xargs $(PERL) -w scripts/checkversion.pl + +namespacecheck: + $(PERL) $(srctree)/scripts/namespace.pl + +endif #ifeq ($(config-targets),1) +endif #ifeq ($(mixed-targets),1) + +PHONY += checkstack +checkstack: + $(OBJDUMP) -d busybox $$(find . -name '*.ko') | \ + $(PERL) $(src)/scripts/checkstack.pl $(ARCH) + +kernelrelease: + $(if $(wildcard .kernelrelease), $(Q)echo $(KERNELRELEASE), \ + $(error kernelrelease not valid - run 'make *config' to update it)) +kernelversion: + @echo $(KERNELVERSION) + +# Single targets +# --------------------------------------------------------------------------- +# Single targets are compatible with: +# - build whith mixed source and output +# - build with separate output dir 'make O=...' +# - external modules +# +# target-dir => where to store outputfile +# build-dir => directory in kernel source tree to use + +ifeq ($(KBUILD_EXTMOD),) + build-dir = $(patsubst %/,%,$(dir $@)) + target-dir = $(dir $@) +else + zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@))) + build-dir = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash)) + target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@)) +endif + +%.s: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.i: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.o: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.lst: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.s: %.S prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.o: %.S prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) + +# Modules +%/: prepare scripts FORCE + $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ + $(build)=$(build-dir) +/: prepare scripts FORCE + $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ + $(build)=$(build-dir) + +%.ko: prepare scripts FORCE + $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ + $(build)=$(build-dir) $(@:.ko=.o) + $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost + +# FIXME Should go into a make.lib or something +# =========================================================================== + +quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN $(wildcard $(rm-dirs))) + cmd_rmdirs = rm -rf $(rm-dirs) + +quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files))) + cmd_rmfiles = rm -f $(rm-files) + + +a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(AFLAGS_KERNEL) \ + $(NOSTDINC_FLAGS) $(CPPFLAGS) \ + $(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o) + +quiet_cmd_as_o_S = AS $@ +cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< + +# read all saved command lines + +targets := $(wildcard $(sort $(targets))) +cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) + +ifneq ($(cmd_files),) + $(cmd_files): ; # Do not try to update included dependency files + include $(cmd_files) +endif + +# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.clean obj=dir +# Usage: +# $(Q)$(MAKE) $(clean)=dir +clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj + +endif # skip-makefile + +PHONY += FORCE +FORCE: + +-include $(srctree)/Makefile.custom + +# Declare the contents of the .PHONY variable as phony. We keep that +# information in a variable se we can use it in if_changed and friends. +.PHONY: $(PHONY) + +rootfs: + cd _install + find . | cpio -o --format=newc > ../rootfs.img + cd .. diff --git a/busybox/Makefile.custom b/busybox/Makefile.custom new file mode 100644 index 00000000000000..6f679c4e12b4cb --- /dev/null +++ b/busybox/Makefile.custom @@ -0,0 +1,201 @@ +# ========================================================================== +# Build system +# ========================================================================== + +busybox.links: $(srctree)/applets/busybox.mkll $(objtree)/include/autoconf.h include/applets.h + $(Q)-$(SHELL) $^ > $@ + +busybox.cfg.suid: $(srctree)/applets/busybox.mksuid $(objtree)/include/autoconf.h include/applets.h + $(Q)-SUID="yes" $(SHELL) $^ > $@ +busybox.cfg.nosuid: $(srctree)/applets/busybox.mksuid $(objtree)/include/autoconf.h include/applets.h + $(Q)-SUID="DROP" $(SHELL) $^ > $@ + +.PHONY: install +ifeq ($(CONFIG_INSTALL_APPLET_DONT),y) +INSTALL_OPTS:= --none +endif +ifeq ($(CONFIG_INSTALL_APPLET_SYMLINKS),y) +INSTALL_OPTS:= --symlinks +endif +ifeq ($(CONFIG_INSTALL_APPLET_HARDLINKS),y) +INSTALL_OPTS:= --hardlinks +endif +ifeq ($(CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS),y) +ifeq ($(CONFIG_INSTALL_SH_APPLET_SYMLINK),y) +INSTALL_OPTS:= --sw-sh-sym +endif +ifeq ($(CONFIG_INSTALL_SH_APPLET_HARDLINK),y) +INSTALL_OPTS:= --sw-sh-hard +endif +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) +ifeq ($(strip $(CONFIG_FEATURE_SUID)),y) + @echo + @echo + @echo -------------------------------------------------- + @echo You will probably need to make your busybox binary + @echo setuid root to ensure all configured applets will + @echo work properly. + @echo -------------------------------------------------- + @echo +endif + +install-noclobber: INSTALL_OPTS+=--noclobber +install-noclobber: install + +uninstall: busybox.links + rm -f $(CONFIG_PREFIX)/bin/busybox + for i in `cat busybox.links` ; do rm -f $(CONFIG_PREFIX)$$i; done +ifneq ($(strip $(DO_INSTALL_LIBS)),n) + for i in $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS); do \ + rm -f $(CONFIG_PREFIX)$$i; \ + done +endif + +# Not very elegant: copies testsuite to objdir... +# (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)" + +.PHONY: release +release: distclean + cd ..; \ + rm -r -f busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION); \ + cp -pPR busybox busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) && { \ + find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type d \ + -name .svn \ + -print \ + -exec rm -r -f {} \; ; \ + find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type d \ + -name .git \ + -print \ + -exec rm -r -f {} \; ; \ + find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type f \ + -name .gitignore \ + -print \ + -exec rm -f {} \; ; \ + find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type f \ + -name .\#* \ + -print \ + -exec rm -f {} \; ; \ + tar -czf busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION).tar.gz \ + busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ ; } + +.PHONY: checkhelp +checkhelp: + $(Q)$(srctree)/scripts/checkhelp.awk \ + $(patsubst %,$(srctree)/%,$(wildcard $(patsubst %,%/Config.in,$(busybox-dirs) ./))) + +.PHONY: sizes +sizes: busybox_unstripped + $(NM) --size-sort $(<) + +.PHONY: bloatcheck +bloatcheck: busybox_old busybox_unstripped + @$(srctree)/scripts/bloat-o-meter busybox_old busybox_unstripped + @$(CROSS_COMPILE)size busybox_old busybox_unstripped + +.PHONY: baseline +baseline: busybox_unstripped + @mv busybox_unstripped busybox_old + +.PHONY: objsizes +objsizes: busybox_unstripped + $(srctree)/scripts/objsizes + +.PHONY: stksizes +stksizes: busybox_unstripped + $(CROSS_COMPILE)objdump -d busybox_unstripped | $(srctree)/scripts/checkstack.pl $(ARCH) | uniq + +.PHONY: bigdata +bigdata: busybox_unstripped + $(CROSS_COMPILE)nm --size-sort busybox_unstripped | grep -vi ' [trw] ' + +# Documentation Targets +.PHONY: doc +doc: docs/busybox.pod docs/BusyBox.txt docs/busybox.1 docs/BusyBox.html + +# FIXME: Doesn't belong here + cmd_doc = + quiet_cmd_doc = $(Q)echo " DOC $(@F)" +silent_cmd_doc = +disp_doc = $($(quiet)cmd_doc) + +# sed adds newlines after "Options:" etc, +# this is needed in order to get good BusyBox.{1,txt,html} +docs/busybox.pod: $(srctree)/docs/busybox_header.pod \ + include/usage.h \ + $(srctree)/docs/busybox_footer.pod \ + applets/usage_pod + $(disp_doc) + $(Q)-mkdir -p docs + $(Q)-( \ + cat $(srctree)/docs/busybox_header.pod; \ + echo; \ + applets/usage_pod | sed 's/^[A-Za-z][A-Za-z ]*[a-z]:$$/&\n/'; \ + cat $(srctree)/docs/busybox_footer.pod; \ + ) > docs/busybox.pod + +docs/BusyBox.txt: docs/busybox.pod + $(disp_doc) + $(Q)-mkdir -p docs + $(Q)-pod2text $< > $@ + +docs/busybox.1: docs/busybox.pod + $(disp_doc) + $(Q)-mkdir -p docs + $(Q)-pod2man --center=busybox --release="version $(KERNELVERSION)" $< > $@ + +docs/BusyBox.html: docs/busybox.net/BusyBox.html + $(disp_doc) + $(Q)-mkdir -p docs + $(Q)-rm -f docs/BusyBox.html + $(Q)-cp docs/busybox.net/BusyBox.html docs/BusyBox.html + +docs/busybox.net/BusyBox.html: docs/busybox.pod + $(Q)-mkdir -p docs/busybox.net + $(Q)-pod2html --noindex $< > $@ + $(Q)-rm -f pod2htm* + +# documentation, cross-reference +# Modern distributions already ship synopsis packages (e.g. debian) +# If you have an old distribution go to http://synopsis.fresco.org/ +syn_tgt = $(wildcard $(patsubst %,%/*.c,$(busybox-alldirs))) +syn = $(patsubst %.c, %.syn, $(syn_tgt)) + +comma:= , +brace_open:= ( +brace_close:= ) + +SYN_CPPFLAGS := $(strip $(CPPFLAGS) $(EXTRA_CPPFLAGS)) +SYN_CPPFLAGS := $(subst $(brace_open),\$(brace_open),$(SYN_CPPFLAGS)) +SYN_CPPFLAGS := $(subst $(brace_close),\$(brace_close),$(SYN_CPPFLAGS)) +#SYN_CPPFLAGS := $(subst ",\",$(SYN_CPPFLAGS)) +#") +#SYN_CPPFLAGS := [$(patsubst %,'%'$(comma),$(SYN_CPPFLAGS))''] + +%.syn: %.c + synopsis -p C -l Comments.SSDFilter,Comments.Previous -Wp,preprocess=True,cppflags="'$(SYN_CPPFLAGS)'" -o $@ $< + +.PHONY: html +html: $(syn) + synopsis -f HTML -Wf,title="'BusyBox Documentation'" -o $@ $^ + +-include $(srctree)/Makefile.local diff --git a/busybox/Makefile.flags b/busybox/Makefile.flags new file mode 100644 index 00000000000000..6f6142cc5d39ae --- /dev/null +++ b/busybox/Makefile.flags @@ -0,0 +1,195 @@ +# ========================================================================== +# Build system +# ========================================================================== + +BB_VER = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) +export BB_VER +SKIP_STRIP ?= n + +# -std=gnu99 needed for [U]LLONG_MAX on some systems +CPPFLAGS += $(call cc-option,-std=gnu99,) + +CPPFLAGS += \ + -Iinclude -Ilibbb \ + $(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include -I$(srctree)/libbb) \ + -include include/autoconf.h \ + -D_GNU_SOURCE -DNDEBUG \ + $(if $(CONFIG_LFS),-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) \ + -D"BB_VER=KBUILD_STR($(BB_VER))" + +CFLAGS += $(call cc-option,-Wall,) +CFLAGS += $(call cc-option,-Wshadow,) +CFLAGS += $(call cc-option,-Wwrite-strings,) +CFLAGS += $(call cc-option,-Wundef,) +CFLAGS += $(call cc-option,-Wstrict-prototypes,) +CFLAGS += $(call cc-option,-Wunused -Wunused-parameter,) +CFLAGS += $(call cc-option,-Wunused-function -Wunused-value,) +CFLAGS += $(call cc-option,-Wmissing-prototypes -Wmissing-declarations,) +CFLAGS += $(call cc-option,-Wno-format-security,) +# warn about C99 declaration after statement +CFLAGS += $(call cc-option,-Wdeclaration-after-statement,) +# If you want to add more -Wsomething above, make sure that it is +# still possible to build bbox without warnings. + +ifeq ($(CONFIG_WERROR),y) +CFLAGS += $(call cc-option,-Werror,) +## TODO: +## gcc version 4.4.0 20090506 (Red Hat 4.4.0-4) (GCC) is a PITA: +## const char *ptr; ... off_t v = *(off_t*)ptr; -> BOOM +## and no easy way to convince it to shut the hell up. +## We have a lot of such things all over the place. +## Classic *(off_t*)(void*)ptr does not work, +## and I am unwilling to do crazy gcc specific ({ void *ppp = ...; }) +## stuff in macros. This would obfuscate the code too much. +## Maybe try __attribute__((__may_alias__))? +#CFLAGS += $(call cc-ifversion, -eq, 0404, -fno-strict-aliasing) +endif +# gcc 3.x emits bogus "old style proto" warning on find.c:alloc_action() +CFLAGS += $(call cc-ifversion, -ge, 0400, -Wold-style-definition) + +CFLAGS += $(call cc-option,-fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer -ffunction-sections -fdata-sections,) +# -fno-guess-branch-probability: prohibit pseudo-random guessing +# of branch probabilities (hopefully makes bloatcheck more stable): +CFLAGS += $(call cc-option,-fno-guess-branch-probability,) +CFLAGS += $(call cc-option,-funsigned-char -static-libgcc,) +CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=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.. +#CFLAGS += $(call cc-option,-Wconversion,) + +ifneq ($(CONFIG_DEBUG),y) +CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) +else +CFLAGS += $(call cc-option,-g,) +#CFLAGS += "-D_FORTIFY_SOURCE=2" +ifeq ($(CONFIG_DEBUG_PESSIMIZE),y) +CFLAGS += $(call cc-option,-O0,) +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 +ARCH_FPIE ?= -fpie +ARCH_PIE ?= -pie + +# Usage: $(eval $(call pkg_check_modules,VARIABLE-PREFIX,MODULES)) +define pkg_check_modules +$(1)_CFLAGS := $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags $(2)) +$(1)_LIBS := $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --libs $(2)) +endef + +ifeq ($(CONFIG_BUILD_LIBBUSYBOX),y) +# on i386: 14% smaller libbusybox.so +# (code itself is 9% bigger, we save on relocs/PLT/GOT) +CFLAGS += $(ARCH_FPIC) +# and another 4% reduction of libbusybox.so: +# (external entry points must be marked EXTERNALLY_VISIBLE) +CFLAGS += $(call cc-option,-fvisibility=hidden) +endif + +ifeq ($(CONFIG_STATIC),y) +CFLAGS_busybox += -static +PKG_CONFIG_FLAGS += --static +endif + +ifeq ($(CONFIG_PIE),y) +CFLAGS_busybox += $(ARCH_PIE) +CFLAGS += $(ARCH_FPIE) +endif + +ifneq ($(CONFIG_EXTRA_CFLAGS),) +CFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_CFLAGS))) +#")) +endif + +# Note: both "" (string consisting of two quote chars) and empty string +# are possible, and should be skipped below. +ifneq ($(subst "",,$(CONFIG_SYSROOT)),) +CFLAGS += --sysroot=$(CONFIG_SYSROOT) +export SYSROOT=$(CONFIG_SYSROOT) +endif + +# Android has no separate crypt library +# gcc-4.2.1 fails if we try to feed C source on stdin: +# echo 'int main(void){return 0;}' | $(CC) $(CFLAGS) -lcrypt -o /dev/null -xc - +# fall back to using a temp file: +CRYPT_AVAILABLE := $(shell echo 'int main(void){return 0;}' >crypttest.c; $(CC) $(CFLAGS) -lcrypt -o /dev/null crypttest.c >/dev/null 2>&1 && echo "y"; rm crypttest.c) +ifeq ($(CRYPT_AVAILABLE),y) +LDLIBS += m crypt +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) +LDLIBS += pam pam_misc +endif + +ifeq ($(CONFIG_SELINUX),y) +SELINUX_PC_MODULES = libselinux libsepol +$(eval $(call pkg_check_modules,SELINUX,$(SELINUX_PC_MODULES))) +CPPFLAGS += $(SELINUX_CFLAGS) +LDLIBS += $(if $(SELINUX_LIBS),$(SELINUX_LIBS:-l%=%),$(SELINUX_PC_MODULES:lib%=%)) +endif + +ifeq ($(CONFIG_FEATURE_NSLOOKUP_BIG),y) +LDLIBS += resolv +endif + +ifeq ($(CONFIG_EFENCE),y) +LDLIBS += efence +endif + +ifeq ($(CONFIG_DMALLOC),y) +LDLIBS += dmalloc +endif + +# If a flat binary should be built, CFLAGS_busybox="-elf2flt" +# env var should be set for make invocation. +# Here we check whether CFLAGS_busybox indeed contains that flag. +# (For historical reasons, we also check LDFLAGS, which doesn't +# seem to be entirely correct variable to put "-elf2flt" into). +W_ELF2FLT = -elf2flt +ifneq (,$(findstring $(W_ELF2FLT),$(LDFLAGS) $(CFLAGS_busybox))) +SKIP_STRIP = y +endif + +ifneq ($(CONFIG_EXTRA_LDFLAGS),) +LDFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_LDFLAGS))) +#")) +endif + +ifneq ($(CONFIG_EXTRA_LDLIBS),) +LDLIBS += $(strip $(subst ",,$(CONFIG_EXTRA_LDLIBS))) +#")) +endif + +# Busybox is a stack-fatty so make sure we increase default size +# TODO: use "make stksizes" to find & fix big stack users +# (we stole scripts/checkstack.pl from the kernel... thanks guys!) +# Reduced from 20k to 16k in 1.9.0. +FLTFLAGS += -s 16000 diff --git a/busybox/Makefile.help b/busybox/Makefile.help new file mode 100644 index 00000000000000..6a23e2a8077dfe --- /dev/null +++ b/busybox/Makefile.help @@ -0,0 +1,44 @@ +# ========================================================================== +# Build system +# ========================================================================== + +help: + @echo 'Cleaning:' + @echo ' clean - delete temporary files created by build' + @echo ' distclean - delete all non-source files (including .config)' + @echo ' doc-clean - delete all generated documentation' + @echo + @echo 'Build:' + @echo ' all - Executable and documentation' + @echo ' busybox - the swiss-army executable' + @echo ' doc - docs/BusyBox.{txt,html,1}' + @echo ' html - create html-based cross-reference' + @echo + @echo 'Configuration:' + @echo ' allnoconfig - disable all symbols in .config' + @echo ' allyesconfig - enable all symbols in .config (see defconfig)' + @echo ' config - text based configurator (of last resort)' + @echo ' defconfig - set .config to largest generic configuration' + @echo ' menuconfig - interactive curses-based configurator' + @echo ' oldconfig - resolve any unresolved symbols in .config' + @$(if $(boards), \ + $(foreach b, $(boards), \ + printf " %-21s - Build for %s\\n" $(b) $(subst _defconfig,,$(b));) \ + echo '') + @echo + @echo 'Installation:' + @echo ' install - install busybox into CONFIG_PREFIX' + @echo ' uninstall' + @echo + @echo 'Development:' + @echo ' baseline - create busybox_old for bloatcheck.' + @echo ' bloatcheck - show size difference between old and new versions' + @echo ' check - run the test suite for all applets' + @echo ' checkhelp - check for missing help-entries in Config.in' + @echo ' randconfig - generate a random configuration' + @echo ' release - create a distribution tarball' + @echo ' sizes - show size of all enabled busybox symbols' + @echo ' objsizes - show size of each .o object built' + @echo ' bigdata - show data objects, biggest first' + @echo ' stksizes - show stack users, biggest first' + @echo diff --git a/busybox/NOFORK_NOEXEC.sh b/busybox/NOFORK_NOEXEC.sh new file mode 100644 index 00000000000000..f4eeeef87a78cc --- /dev/null +++ b/busybox/NOFORK_NOEXEC.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +exec >NOFORK_NOEXEC.lst1 + +false && grep -Fv 'NOFORK' NOFORK_NOEXEC.lst \ +| grep -v 'noexec.' | grep -v 'noexec$' \ +| grep -v ' suid' \ +| grep -v ' daemon' \ +| grep -v ' longterm' \ +| grep rare + +echo === nofork candidate +grep -F 'nofork candidate' NOFORK_NOEXEC.lst \ + +echo === noexec candidate +grep -F 'noexec candidate' NOFORK_NOEXEC.lst \ + +echo === ^C +grep -F '^C' NOFORK_NOEXEC.lst \ +| grep -F ' - ' \ + +echo === talks +grep -F 'talks' NOFORK_NOEXEC.lst \ +| grep -F ' - ' \ + +echo === +grep -Fv 'NOFORK' NOFORK_NOEXEC.lst \ +| grep '^[^ ][^ ]* - ' \ +| grep -v 'noexec.' | grep -v ' - noexec$' \ +| grep -v ' suid' \ +| grep -v ' daemon' \ +| grep -v 'longterm' \ +| grep -v 'interactive' \ +| grep -v 'hardware' \ diff --git a/busybox/README b/busybox/README new file mode 100644 index 00000000000000..ada5935b9a48db --- /dev/null +++ b/busybox/README @@ -0,0 +1,204 @@ +Please see the LICENSE file for details on copying and usage. +Please refer to the INSTALL file for instructions on how to build. + +What is busybox: + + BusyBox combines tiny versions of many common UNIX utilities into a single + small executable. It provides minimalist replacements for most of the + utilities you usually find in bzip2, coreutils, dhcp, diffutils, e2fsprogs, + file, findutils, gawk, grep, inetutils, less, modutils, net-tools, procps, + sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. The utilities + in BusyBox often have fewer options than their full-featured cousins; + however, the options that are included provide the expected functionality + and behave very much like their larger counterparts. + + BusyBox has been written with size-optimization and limited resources in + mind, both to produce small binaries and to reduce run-time memory usage. + Busybox is also extremely modular so you can easily include or exclude + commands (or features) at compile time. This makes it easy to customize + embedded systems; to create a working system, just add /dev, /etc, and a + Linux kernel. Busybox (usually together with uClibc) has also been used as + a component of "thin client" desktop systems, live-CD distributions, rescue + disks, installers, and so on. + + BusyBox provides a fairly complete POSIX environment for any small system, + both embedded environments and more full featured systems concerned about + space. Busybox is slowly working towards implementing the full Single Unix + Specification V3 (http://www.opengroup.org/onlinepubs/009695399/), but isn't + there yet (and for size reasons will probably support at most UTF-8 for + internationalization). We are also interested in passing the Linux Test + Project (http://ltp.sourceforge.net). + +---------------- + +Using busybox: + + BusyBox is extremely configurable. This allows you to include only the + components and options you need, thereby reducing binary size. Run 'make + config' or 'make menuconfig' to select the functionality that you wish to + enable. (See 'make help' for more commands.) + + The behavior of busybox is determined by the name it's called under: as + "cp" it behaves like cp, as "sed" it behaves like sed, and so on. Called + as "busybox" it takes the second argument as the name of the applet to + run (I.E. "./busybox ls -l /proc"). + + The "standalone shell" mode is an easy way to try out busybox; this is a + command shell that calls the built-in applets without needing them to be + installed in the path. (Note that this requires /proc to be mounted, if + testing from a boot floppy or in a chroot environment.) + + The build automatically generates a file "busybox.links", which is used by + 'make install' to create symlinks to the BusyBox binary for all compiled in + commands. This uses the CONFIG_PREFIX environment variable to specify + where to install, and installs hardlinks or symlinks depending + on the configuration preferences. (You can also manually run + the install script at "applets/install.sh"). + +---------------- + +Downloading the current source code: + + Source for the latest released version, as well as daily snapshots, can always + be downloaded from + + http://busybox.net/downloads/ + + You can browse the up to the minute source code and change history online. + + http://git.busybox.net/busybox/ + + Anonymous GIT access is available. For instructions, check out: + + http://www.busybox.net/source.html + + For those that are actively contributing and would like to check files in, + see: + + http://busybox.net/developer.html + + The developers also have a bug and patch tracking system + (https://bugs.busybox.net) although posting a bug/patch to the mailing list + is generally a faster way of getting it fixed, and the complete archive of + what happened is the git changelog. + + Note: if you want to compile busybox in a busybox environment you must + select CONFIG_DESKTOP. + +---------------- + +Getting help: + + when you find you need help, you can check out the busybox mailing list + archives at http://busybox.net/lists/busybox/ or even join + the mailing list if you are interested. + +---------------- + +Bugs: + + if you find bugs, please submit a detailed bug report to the busybox mailing + list at busybox@busybox.net. a well-written bug report should include a + transcript of a shell session that demonstrates the bad behavior and enables + anyone else to duplicate the bug on their own machine. the following is such + an example: + + to: busybox@busybox.net + from: diligent@testing.linux.org + subject: /bin/date doesn't work + + package: busybox + version: 1.00 + + when i execute busybox 'date' it produces unexpected results. + with gnu date i get the following output: + + $ date + fri oct 8 14:19:41 mdt 2004 + + but when i use busybox date i get this instead: + + $ date + illegal instruction + + i am using debian unstable, kernel version 2.4.25-vrs2 on a netwinder, + and the latest uclibc from cvs. + + -diligent + + note the careful description and use of examples showing not only what + busybox does, but also a counter example showing what an equivalent app + does (or pointing to the text of a relevant standard). Bug reports lacking + such detail may never be fixed... Thanks for understanding. + +---------------- + +Portability: + + Busybox is developed and tested on Linux 2.4 and 2.6 kernels, compiled + with gcc (the unit-at-a-time optimizations in version 3.4 and later are + worth upgrading to get, but older versions should work), and linked against + uClibc (0.9.27 or greater) or glibc (2.2 or greater). In such an + environment, the full set of busybox features should work, and if + anything doesn't we want to know about it so we can fix it. + + There are many other environments out there, in which busybox may build + and run just fine. We just don't test them. Since busybox consists of a + large number of more or less independent applets, portability is a question + of which features work where. Some busybox applets (such as cat and rm) are + highly portable and likely to work just about anywhere, while others (such as + insmod and losetup) require recent Linux kernels with recent C libraries. + + Earlier versions of Linux and glibc may or may not work, for any given + configuration. Linux 2.2 or earlier should mostly work (there's still + some support code in things like mount.c) but this is no longer regularly + tested, and inherently won't support certain features (such as long files + and --bind mounts). The same is true for glibc 2.0 and 2.1: expect a higher + testing and debugging burden using such old infrastructure. (The busybox + developers are not very interested in supporting these older versions, but + will probably accept small self-contained patches to fix simple problems.) + + Some environments are not recommended. Early versions of uClibc were buggy + and missing many features: upgrade. Linking against libc5 or dietlibc is + not supported and not interesting to the busybox developers. (The first is + obsolete and has no known size or feature advantages over uClibc, the second + has known bugs that its developers have actively refused to fix.) Ancient + Linux kernels (2.0.x and earlier) are similarly uninteresting. + + In theory it's possible to use Busybox under other operating systems (such as + MacOS X, Solaris, Cygwin, or the BSD Fork Du Jour). This generally involves + a different kernel and a different C library at the same time. While it + should be possible to port the majority of the code to work in one of + these environments, don't be surprised if it doesn't work out of the box. If + you're into that sort of thing, start small (selecting just a few applets) + and work your way up. + + In 2005 Shaun Jackman has ported busybox to a combination of newlib + and libgloss, and some of his patches have been integrated. + +Supported hardware: + + BusyBox in general will build on any architecture supported by gcc. We + support both 32 and 64 bit platforms, and both big and little endian + systems. + + 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, 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 + architectures supported by the kernel. + +---------------- + +Please feed suggestions, bug reports, insults, and bribes back to the busybox +mailing list: + + busybox@busybox.net + +and/or maintainer: + + Denys Vlasenko + diff --git a/busybox/TODO b/busybox/TODO new file mode 100644 index 00000000000000..72ae0f88db7b31 --- /dev/null +++ b/busybox/TODO @@ -0,0 +1,258 @@ +Busybox TODO + +Harvest patches from +http://git.openembedded.org/cgit.cgi/openembedded/tree/recipes/busybox/ +https://dev.openwrt.org/browser/trunk/package/busybox/patches/ + + +Stuff that needs to be done. This is organized by who plans to get around to +doing it eventually, but that doesn't mean they "own" the item. If you want to +do one of these bounce an email off the person it's listed under to see if they +have any suggestions how they plan to go about it, and to minimize conflicts +between your work and theirs. But otherwise, all of these are fair game. + +Rob Landley suggested this: + Implement bb_realpath() that can handle NULL on non-glibc. + + sh + The command shell situation is a mess. We have two different + shells that don't really share any code, and the "standalone shell" doesn't + work all that well (especially not in a chroot environment), due to apps not + being reentrant. + + Do a SUSv3 audit + Look at the full Single Unix Specification version 3 (available online at + "http://www.opengroup.org/onlinepubs/009695399/nfindex.html") and + figure out which of our apps are compliant, and what we're missing that + we might actually care about. + + Even better would be some kind of automated compliance test harness that + exercises each command line option and the various corner cases. + + Internationalization + How much internationalization should we do? + + The low hanging fruit is UTF-8 character set support. We should do this. + See TODO_unicode file. + + We also have lots of hardwired english text messages. Consolidating this + into some kind of message table not only makes translation easier, but + also allows us to consolidate redundant (or close) strings. + + We probably don't want to be bloated with locale support. (Not unless we + can cleanly export it from our underlying C library without having to + concern ourselves with it directly. Perhaps a few specific things like a + config option for "date" are low hanging fruit here?) + + What level should things happen at? How much do we care about + internationalizing the text console when X11 and xterms are so much better + at it? (There's some infrastructure here we don't implement: The + "unicode_start" and "unicode_stop" shell scripts need "vt-is-UTF8" and a + --unicode option to loadkeys. That implies a real loadkeys/dumpkeys + implementation to replace loadkmap/dumpkmap. Plus messing with console font + loading. Is it worth it, or do we just say "use X"?) + + Individual compilation of applets. + It would be nice if busybox had the option to compile to individual applets, + for people who want an alternate implementation less bloated than the gnu + utils (or simply with less political baggage), but without it being one big + executable. + + Turning libbb into a real dll is another possibility, especially if libbb + could export some of the other library interfaces we've already more or less + got the code for (like zlib). + + buildroot - Make a "dogfood" option + Busybox 1.1 will be capable of replacing most gnu packages for real world + use, such as developing software or in a live CD. It needs wider testing. + + Busybox should now be able to replace bzip2, coreutils, e2fsprogs, file, + findutils, gawk, grep, inetutils, less, modutils, net-tools, patch, procps, + sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. The resulting + system should be self-hosting (I.E. able to rebuild itself from source + code). This means it would need (at least) binutils, gcc, and make, or + equivalents. + + It would be a good "eating our own dogfood" test if buildroot had the option + of using a "make allyesconfig" busybox instead of the all of the above + packages. Anything that's wrong with the resulting system, we can fix. (It + would be nice to be able to upgrade busybox to be able to replace bash and + diffutils as well, but we're not there yet.) + + One example of an existing system that does this already is Firmware Linux: + http://www.landley.net/code/firmware + + initramfs + Busybox should have a sample initramfs build script. This depends on + shell, mdev, and switch_root. + + mkdep + Write a mkdep that doesn't segfault if there's a directory it doesn't + have permission to read, isn't based on manually editing the output of + lexx and yacc, doesn't make such a mess under include/config, etc. + + Group globals into unions of structures. + Go through and turn all the global and static variables into structures, + and have all those structures be in a big union shared between processes, + so busybox uses less bss. (This is a big win on nommu machines.) See + sed.c and mdev.c for examples. + + Go through bugs.busybox.net and close out all of that somehow. + This one's open to everybody, but I'll wind up doing it... + +Bernhard Reutner-Fischer suggests to look at these: + New debug options: + -Wlarger-than-127 + Cleanup any big users + Collate BUFSIZ IOBUF_SIZE MY_BUF_SIZE PIPE_PROGRESS_SIZE BUFSIZE PIPESIZE + make bb_common_bufsiz1 configurable, size wise. + make pipesize configurable, size wise. + Use bb_common_bufsiz1 throughout applets! + +As yet unclaimed: + +---- +diff + Make sure we handle empty files properly: + From the patch man page: + + you can remove a file by sending out a context diff that compares + the file to be deleted with an empty file dated the Epoch. The + file will be removed unless patch is conforming to POSIX and the + -E or --remove-empty-files option is not given. +--- +patch + Should have simple fuzz factor support to apply patches at an offset which + shouldn't take up too much space. + + And while we're at it, a new patch filename quoting format is apparently + coming soon: http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2 + +Architectural issues: + +bb_close() with fsync() + We should have a bb_close() in place of normal close, with a CONFIG_ option + to not just check the return value of close() for an error, but fsync(). + Close can't reliably report anything useful because if write() accepted the + data then it either went out to the network or it's in cache or a pipe + buffer. Either way, there's no guarantee it'll make it to its final + destination before close() gets called, so there's no guarantee that any + error will be reported. + + You need to call fsync() if you care about errors that occur after write(), + but that can have a big performance impact. So make it a config option. +--- +Unify archivers + Lots of archivers have the same general infrastructure. The directory + traversal code should be factored out, and the guts of each archiver could + be some setup code and a series of callbacks for "add this file", + "add this directory", "add this symlink" and so on. + + This could clean up tar and zip, and make it cheaper to add cpio and ar + write support, and possibly even cheaply add things like mkisofs or + mksquashfs someday, if they become relevant. +--- +Text buffer support. + Several existing applets (sort, vi, less...) read + a whole file into memory and act on it. Use open_read_close(). +--- +Memory Allocation + We have a CONFIG_BUFFER mechanism that lets us select whether to do memory + allocation on the stack or the heap. Unfortunately, we're not using it much. + We need to audit our memory allocations and turn a lot of malloc/free calls + into RESERVE_CONFIG_BUFFER/RELEASE_CONFIG_BUFFER. + For a start, see e.g. make EXTRA_CFLAGS=-Wlarger-than-64 + + And while we're at it, many of the CONFIG_FEATURE_CLEAN_UP #ifdefs will be + optimized out by the compiler in the stack allocation case (since there's no + free for an alloca()), and this means that various cleanup loops that just + call free might also be optimized out by the compiler if written right, so + we can yank those #ifdefs too, and generally clean up the code. +--- +FEATURE_CLEAN_UP + This is more an unresolved issue than a to-do item. More thought is needed. + + Normally we rely on exit() to free memory, close files and unmap segments + for us. This makes most calls to free(), close(), and unmap() optional in + busybox applets that don't intend to run for very long, and optional stuff + can be omitted to save size. + + The idea was raised that we could simulate fork/exit with setjmp/longjmp + for _really_ brainless embedded systems, or speed up the standalone shell + by not forking. Doing so would require a reliable FEATURE_CLEAN_UP. + Unfortunately, this isn't as easy as it sounds. + + The problem is, lots of things exit(), sometimes unexpectedly (xmalloc()) + and sometimes reliably (bb_perror_msg_and_die() or show_usage()). This + jumps out of the normal flow control and bypasses any cleanup code we + put at the end of our applets. + + It's possible to add hooks to libbb functions like xmalloc() and xopen() + to add their entries to a linked list, which could be traversed and + freed/closed automatically. (This would need to be able to free just the + entries after a checkpoint to be usable for a forkless standalone shell. + You don't want to free the shell's own resources.) + + Right now, FEATURE_CLEAN_UP is more or less a debugging aid, to make things + like valgrind happy. It's also documentation of _what_ we're trusting + exit() to clean up for us. But new infrastructure to auto-free stuff would + render the existing FEATURE_CLEAN_UP code redundant. + + For right now, exit() handles it just fine. + + +Minor stuff: + watchdog.c could autodetect the timer duration via: + if(!ioctl (fd, WDIOC_GETTIMEOUT, &tmo)) timer_duration = 1 + (tmo / 2); + Unfortunately, that needs linux/watchdog.h and that contains unfiltered + kernel types on some distros, which breaks the build. +--- + use bb_error_msg where appropriate: See + egrep "(printf.*\([[:space:]]*(stderr|2)|[^_]write.*\([[:space:]]*(stderr|2))" +--- + use bb_perror_msg where appropriate: See + egrep "[^_]perror" +--- + possible code duplication ingroup() and is_a_group_member() +--- + Move __get_hz() to a better place and (re)use it in route.c, ash.c +--- + See grep -r strtod + Alot of duplication that wants cleanup. +--- + unify progress_meter. wget, flash_eraseall, pipe_progress, fbsplash, setfiles. +--- + support start-stop-daemon -d +--- + +(TODO list after discussion 11.05.2009) + +* shrink tc/brctl/ip + tc/brctl seem like fairly large things to try and tackle in your timeframe, + and i think people have posted attempts in the past. Adding additional + options to ip though seems reasonable. + +* add tests for some applets + +* implement POSIX utilities and audit them for POSIX conformance. then + audit them for GNU conformance. then document all your findings in a new + doc/conformance.txt file while perhaps implementing some of the missing + features. + you can find the latest POSIX documentation (1003.1-2008) here: + http://www.opengroup.org/onlinepubs/9699919799/ + and the complete list of all utilities that POSIX covers: + http://www.opengroup.org/onlinepubs/9699919799/idx/utilities.html + The first step would to generate a file/matrix what is already archived + (also IPV6) + +* implement 'at' + +* rpcbind (former portmap) or equivalent + so that we don't have to use -o nolock on nfs mounts + +* check IPV6 compliance + +* generate a mini example using kernel+busybox only (+libc) for example + +* more support for advanced linux 2.6.x features, see: iotop + most likely there is more diff --git a/busybox/TODO_unicode b/busybox/TODO_unicode new file mode 100644 index 00000000000000..b310e8d4d04e85 --- /dev/null +++ b/busybox/TODO_unicode @@ -0,0 +1,45 @@ +Already fixed applets: +cal +lsmod +df +dumpleases + +Applets which may need unicode handling (more extensive than sanitizing +of filenames in error messages): + +ls - work in progress +expand, unexpand - uses unicode_strlen, not scrlen +ash, hush through lineedit - uses unicode_strlen, not scrlen +top - need to sanitize process args +ps - need to sanitize process args +less +more +vi +ed +cut +awk +sed +tr +grep egrep fgrep +fold +sort +head, tail +catv - "display nonprinting chars" - what this could mean for unicode? +wc +chat +dumpkmap +last - just line up columns +man +microcom +strings +watch + +Unsure, may need fixing: + +hostname - do we really want to protect against bad chars in it? +patch +addgroup, adduser, delgroup, deluser +telnet +telnetd +od +printf diff --git a/busybox/applets/.gitignore b/busybox/applets/.gitignore new file mode 100644 index 00000000000000..459938d678feda --- /dev/null +++ b/busybox/applets/.gitignore @@ -0,0 +1,3 @@ +/applet_tables +/usage +/usage_pod diff --git a/busybox/applets/Kbuild.src b/busybox/applets/Kbuild.src new file mode 100644 index 00000000000000..3aedbbffef82e4 --- /dev/null +++ b/busybox/applets/Kbuild.src @@ -0,0 +1,57 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +obj-y := +obj-y += applets.o + +hostprogs-y:= +hostprogs-y += usage usage_pod applet_tables + +always:= $(hostprogs-y) + +# Generated files need additional love + +# This trick decreases amount of rebuilds +# if tree is merely renamed/copied +ifeq ($(srctree),$(objtree)) +srctree_slash = +else +srctree_slash = $(srctree)/ +endif + +HOSTCFLAGS_usage.o = -I$(srctree_slash)include -Iinclude +HOSTCFLAGS_usage_pod.o = -I$(srctree_slash)include -Iinclude + +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/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 + +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 include/NUM_APPLETS.h + cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.h + +include/NUM_APPLETS.h: applets/applet_tables + $(call cmd,gen_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/busybox/applets/applet_tables.c b/busybox/applets/applet_tables.c new file mode 100644 index 00000000000000..e3d10c83f1b946 --- /dev/null +++ b/busybox/applets/applet_tables.c @@ -0,0 +1,232 @@ +/* vi: set sw=4 ts=4: */ +/* + * Applet table generator. + * Runs on host and produces include/applet_tables.h + * + * Copyright (C) 2007 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef ARRAY_SIZE +#define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0]))) + +#include "../include/autoconf.h" +#include "../include/applet_metadata.h" + +struct bb_applet { + const char *name; + const char *main; + enum bb_install_loc_t install_loc; + enum bb_suid_t need_suid; + /* true if instead of fork(); exec("applet"); waitpid(); + * one can do fork(); exit(applet_main(argc,argv)); waitpid(); */ + unsigned char noexec; + /* Even nicer */ + /* true if instead of fork(); exec("applet"); waitpid(); + * one can simply call applet_main(argc,argv); */ + unsigned char nofork; +}; + +/* Define struct bb_applet applets[] */ +#include "../include/applets.h" + +enum { NUM_APPLETS = ARRAY_SIZE(applets) }; + +static int cmp_name(const void *a, const void *b) +{ + const struct bb_applet *aa = a; + const struct bb_applet *bb = 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, j; + char tmp1[PATH_MAX], tmp2[PATH_MAX]; + + // 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); + + if (!argv[1]) + return 1; + snprintf(tmp1, PATH_MAX, "%s.%u.new", argv[1], (int) getpid()); + i = open(tmp1, O_WRONLY | O_TRUNC | O_CREAT, 0666); + if (i < 0) + return 1; + dup2(i, 1); + + /* Keep in sync with include/busybox.h! */ + + printf("/* This is a generated file, don't edit */\n\n"); + + printf("#define NUM_APPLETS %u\n", NUM_APPLETS); + if (NUM_APPLETS == 1) { + printf("#define SINGLE_APPLET_STR \"%s\"\n", applets[0].name); + printf("#define SINGLE_APPLET_MAIN %s_main\n", applets[0].main); + } + + 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"); + for (i = 0; i < NUM_APPLETS; i++) { + printf("\"%s\" \"\\0\"\n", applets[i].name); +// if (MAX_APPLET_NAME_LEN < strlen(applets[i].name)) +// MAX_APPLET_NAME_LEN = strlen(applets[i].name); + } + 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++) { + printf("%s_main,\n", applets[i].main); + } + printf("};\n"); + printf("#endif\n\n"); + +#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 + 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"); + i = 0; + while (i < NUM_APPLETS) { + int v = applets[i].install_loc; /* 3 bits */ + if (++i < NUM_APPLETS) + v |= applets[i].install_loc << 4; /* 3 bits */ + printf("0x%02x,\n", v); + i++; + } + 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]) { + FILE *fp; + char line_new[80]; +// char line_old[80]; + + sprintf(line_new, "#define NUM_APPLETS %u\n", NUM_APPLETS); +// 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) { + snprintf(tmp2, PATH_MAX, "%s.%u.new", argv[2], (int) getpid()); + fp = fopen(tmp2, "w"); + if (!fp) + return 1; + fputs(line_new, fp); + if (fclose(fp)) + return 1; +// } + } + + if (fclose(stdout)) + return 1; + if (rename(tmp1, argv[1])) + return 1; + if (rename(tmp2, argv[2])) + return 1; + return 0; +} diff --git a/busybox/applets/applets.c b/busybox/applets/applets.c new file mode 100644 index 00000000000000..98c2b44f5c803b --- /dev/null +++ b/busybox/applets/applets.c @@ -0,0 +1,16 @@ +/* vi: set sw=4 ts=4: */ +/* + * Stub for linking busybox binary against libbusybox. + * + * Copyright (C) 2007 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include "busybox.h" + +#if ENABLE_BUILD_LIBBUSYBOX +int main(int argc UNUSED_PARAM, char **argv) +{ + return lbb_main(argv); +} +#endif diff --git a/busybox/applets/busybox.mkll b/busybox/applets/busybox.mkll new file mode 100644 index 00000000000000..68dbf216262a6c --- /dev/null +++ b/busybox/applets/busybox.mkll @@ -0,0 +1,24 @@ +#!/bin/sh +# Make busybox links list file. + +# input $1: full path to Config.h +# input $2: full path to applets.h +# output (stdout): list of pathnames that should be linked to busybox + +# Maintainer: Larry Doolittle + +export LC_ALL=POSIX +export LC_CTYPE=POSIX + +CONFIG_H=${1:-include/autoconf.h} +APPLETS_H=${2:-include/applets.h} +$HOSTCC -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H | + awk '/^[ \t]*LINK/{ + dir=substr($2,7) + gsub("_","/",dir) + if(dir=="/ROOT") dir="" + file=$3 + gsub("\"","",file) + if (file=="busybox") next + print tolower(dir) "/" file + }' diff --git a/busybox/applets/busybox.mksuid b/busybox/applets/busybox.mksuid new file mode 100644 index 00000000000000..6492c079a325ab --- /dev/null +++ b/busybox/applets/busybox.mksuid @@ -0,0 +1,54 @@ +#!/bin/sh +# Make list of configuration variables regarding suid handling + +# input $1: full path to autoconf.h +# input $2: full path to applets.h +# input $3: full path to .config +# output (stdout): list of CONFIG_ that do or may require suid + +# If the environment variable SUID is not set or set to DROP, +# lists all config options that do not require suid permissions. +# Otherwise, lists all config options for applets that DO or MAY require +# suid permissions. + +# Maintainer: Bernhard Reutner-Fischer + +export LC_ALL=POSIX +export LC_CTYPE=POSIX + +CONFIG_H=${1:-include/autoconf.h} +APPLETS_H=${2:-include/applets.h} +DOT_CONFIG=${3:-.config} + +case ${SUID:-DROP} in +[dD][rR][oO][pP]) USE="DROP" ;; +*) USE="suid" ;; +esac + +$HOSTCC -E -DMAKE_SUID -include $CONFIG_H $APPLETS_H | + awk -v USE=${USE} ' + /^SUID[ \t]/{ + if (USE == "DROP") { + if ($2 != "BB_SUID_DROP") next + } else { + if ($2 == "BB_SUID_DROP") next + } + cfg = $NF + gsub("\"", "", cfg) + cfg = substr(cfg, 8) + s[i++] = "CONFIG_" cfg + s[i++] = "CONFIG_FEATURE_" cfg "_.*" + } + END{ + while (getline < ARGV[2]) { + for (j in s) { + if ($0 ~ "^" s[j] "=y$") { + sub(/=.*/, "") + print + if (s[j] !~ /\*$/) delete s[j] # can drop this applet now + } + } + } + } +' - $DOT_CONFIG + diff --git a/busybox/applets/individual.c b/busybox/applets/individual.c new file mode 100644 index 00000000000000..4c468df06a032f --- /dev/null +++ b/busybox/applets/individual.c @@ -0,0 +1,24 @@ +/* Minimal wrapper to build an individual busybox applet. + * + * Copyright 2005 Rob Landley +#include +#include "usage.h" + +int main(int argc, char **argv) +{ + applet_name = argv[0]; + return APPLET_main(argc, argv); +} + +void bb_show_usage(void) +{ + fputs(APPLET_full_usage "\n", stdout); + exit(EXIT_FAILURE); +} diff --git a/busybox/applets/install.sh b/busybox/applets/install.sh new file mode 100644 index 00000000000000..9aede0f530e277 --- /dev/null +++ b/busybox/applets/install.sh @@ -0,0 +1,137 @@ +#!/bin/sh + +export LC_ALL=POSIX +export LC_CTYPE=POSIX + +prefix=$1 +if [ -z "$prefix" ]; then + echo "usage: applets/install.sh DESTINATION TYPE [OPTS ...]" + echo " TYPE is one of: --symlinks --hardlinks --binaries --scriptwrapper --none" + echo " OPTS is one or more of: --cleanup --noclobber" + exit 1 +fi +shift # Keep only remaining options + +# Source the configuration +. ./.config + +h=`sort busybox.links | uniq` + +sharedlib_dir="0_lib" + +linkopts="" +scriptwrapper="n" +binaries="n" +cleanup="0" +noclobber="0" +while [ ${#} -gt 0 ]; do + case "$1" 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";; + --cleanup) cleanup="1";; + --noclobber) noclobber="1";; + --none) h="";; + *) echo "Unknown install option: $1"; exit 1;; + esac + shift +done + +if [ -n "$DO_INSTALL_LIBS" ] && [ x"$DO_INSTALL_LIBS" != x"n" ]; then + # get the target dir for the libs + # assume it starts with lib + libdir=$($CC -print-file-name=libc.so | \ + sed -n 's%^.*\(/lib[^\/]*\)/libc.so%\1%p') + if test -z "$libdir"; then + libdir=/lib + fi + + mkdir -p "$prefix/$libdir" || exit 1 + 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/`basename $i`" || exit 1 + fi + done +fi + +if [ x"$cleanup" = x"1" ] && [ -e "$prefix/bin/busybox" ]; then + inode=`ls -i "$prefix/bin/busybox" | awk '{print $1}'` + sub_shell_it=` + cd "$prefix" + for d in usr/sbin usr/bin sbin bin; do + pd=$PWD + if [ -d "$d" ]; then + cd "$d" + ls -iL . | grep "^ *$inode" | awk '{print $2}' | env -i xargs rm -f + fi + cd "$pd" + done + ` + exit 0 +fi + +rm -f "$prefix/bin/busybox" || exit 1 +mkdir -p "$prefix/bin" || exit 1 +install -m 755 busybox "$prefix/bin/busybox" || exit 1 + +for i in $h; do + appdir=`dirname "$i"` + app=`basename "$i"` + if [ x"$noclobber" = x"1" ] && [ -e "$prefix/$i" ]; then + echo " $prefix/$i already exists" + continue + fi + mkdir -p "$prefix/$appdir" || exit 1 + if [ x"$scriptwrapper" = x"y" ]; then + if [ x"$swrapall" != x"y" ] && [ x"$i" = x"/bin/sh" ]; then + ln $linkopts busybox "$prefix/$i" || exit 1 + else + rm -f "$prefix/$i" + echo "#!/bin/busybox" >"$prefix/$i" + chmod +x "$prefix/$i" + fi + echo " $prefix/$i" + elif [ x"$binaries" = x"y" ]; then + # Copy the binary over rather + if [ -e "$sharedlib_dir/$app" ]; then + echo " Copying $sharedlib_dir/$app to $prefix/$i" + cp -pPR "$sharedlib_dir/$app" "$prefix/$i" || exit 1 + else + echo "Error: Could not find $sharedlib_dir/$app" + exit 1 + fi + else + if [ x"$linkopts" = x"-f" ]; then + bb_path="$prefix/bin/busybox" + else + case "$appdir" in + /) + bb_path="bin/busybox" + ;; + /bin) + bb_path="busybox" + ;; + /sbin) + bb_path="../bin/busybox" + ;; + /usr/bin | /usr/sbin) + bb_path="../../bin/busybox" + ;; + *) + echo "Unknown installation directory: $appdir" + exit 1 + ;; + esac + fi + echo " $prefix/$i -> $bb_path" + ln $linkopts "$bb_path" "$prefix/$i" || exit 1 + fi +done + +exit 0 diff --git a/busybox/applets/usage.c b/busybox/applets/usage.c new file mode 100644 index 00000000000000..94520ff66fa581 --- /dev/null +++ b/busybox/applets/usage.c @@ -0,0 +1,55 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2008 Denys Vlasenko. + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include +#include +#include + +#include "autoconf.h" + +/* Since we can't use platform.h, have to do this again by hand: */ +#if ENABLE_NOMMU +# define BB_MMU 0 +# define USE_FOR_NOMMU(...) __VA_ARGS__ +# define USE_FOR_MMU(...) +#else +# define BB_MMU 1 +# define USE_FOR_NOMMU(...) +# define USE_FOR_MMU(...) __VA_ARGS__ +#endif + +#include "usage.h" +#define MAKE_USAGE(aname, usage) { aname, usage }, +static struct usage_data { + const char *aname; + const char *usage; +} usage_array[] = { +#include "applets.h" +}; + +static int compare_func(const void *a, const void *b) +{ + const struct usage_data *ua = a; + const struct usage_data *ub = b; + return strcmp(ua->aname, ub->aname); +} + +int main(void) +{ + int i; + int num_messages = sizeof(usage_array) / sizeof(usage_array[0]); + + if (num_messages == 0) + return 0; + + qsort(usage_array, + num_messages, sizeof(usage_array[0]), + compare_func); + for (i = 0; i < num_messages; i++) + write(STDOUT_FILENO, usage_array[i].usage, strlen(usage_array[i].usage) + 1); + + return 0; +} diff --git a/busybox/applets/usage_compressed b/busybox/applets/usage_compressed new file mode 100644 index 00000000000000..36fc2a007218e7 --- /dev/null +++ b/busybox/applets/usage_compressed @@ -0,0 +1,62 @@ +#!/bin/sh + +target="$1" +loc="$2" + +test "$target" || exit 1 +test "$loc" || loc=. +test -x "$loc/usage" || exit 1 +test "$SED" || SED=sed +test "$DD" || DD=dd + +# Some people were bitten by their system lacking a (proper) od +od -v -b /dev/null +if test $? != 0; then + echo 'od tool is not installed or cannot accept "-v -b" options' + exit 1 +fi + +exec >"$target.$$" + +echo '#define UNPACKED_USAGE "" \' +"$loc/usage" | od -v -b \ +| grep -v '^ ' \ +| $SED -e 's/^[^ ]*//' \ + -e 's/ //g' \ + -e '/^$/d' \ + -e 's/\(...\)/\\\1/g' \ + -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 UNPACKED_USAGE_LENGTH `$loc/usage | wc -c`" +echo + +echo '#define PACKED_USAGE \' +## Breaks on big-endian systems! +## # Extra effort to avoid using "od -t x1": -t is not available +## # in non-CONFIG_DESKTOPed busybox od +## +## "$loc/usage" | bzip2 -1 | od -v -x \ +## | $SED -e 's/^[^ ]*//' \ +## -e 's/ //g' \ +## -e '/^$/d' \ +## -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' \ + -e 's/\(...\)/0\1,/g' \ + -e 's/$/ \\/' +echo '' + +mv -- "$target.$$" "$target" diff --git a/busybox/applets/usage_pod.c b/busybox/applets/usage_pod.c new file mode 100644 index 00000000000000..ccc166aed7081f --- /dev/null +++ b/busybox/applets/usage_pod.c @@ -0,0 +1,113 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2009 Denys Vlasenko. + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include +#include +#include +#include +#include + +#include "autoconf.h" + +#define SKIP_applet_main +#define ALIGN1 /* nothing, just to placate applet_tables.h */ +#define ALIGN2 /* nothing, just to placate applet_tables.h */ +#include "applet_tables.h" + +/* Since we can't use platform.h, have to do this again by hand: */ +#if ENABLE_NOMMU +# define BB_MMU 0 +# define USE_FOR_NOMMU(...) __VA_ARGS__ +# define USE_FOR_MMU(...) +#else +# define BB_MMU 1 +# define USE_FOR_NOMMU(...) +# define USE_FOR_MMU(...) __VA_ARGS__ +#endif + +#include "usage.h" +#define MAKE_USAGE(aname, usage) { aname, usage }, +static struct usage_data { + const char *aname; + const char *usage; +} usage_array[] = { +#include "applets.h" +}; + +static int compare_func(const void *a, const void *b) +{ + const struct usage_data *ua = a; + const struct usage_data *ub = b; + return strcmp(ua->aname, ub->aname); +} + +int main(void) +{ + int col, len2; + + int i; + int num_messages = sizeof(usage_array) / sizeof(usage_array[0]); + + if (num_messages == 0) + return 0; + + qsort(usage_array, + num_messages, sizeof(usage_array[0]), + compare_func); + + col = 0; + for (i = 0; i < num_messages; i++) { + len2 = strlen(usage_array[i].aname) + 2; + if (col >= 76 - len2) { + printf(",\n"); + col = 0; + } + if (col == 0) { + col = 6; + printf("\t"); + } else { + printf(", "); + } + printf(usage_array[i].aname); + col += len2; + } + printf("\n\n"); + + printf("=head1 COMMAND DESCRIPTIONS\n\n"); + printf("=over 4\n\n"); + + for (i = 0; i < num_messages; i++) { + if (usage_array[i].aname[0] >= 'a' && usage_array[i].aname[0] <= 'z' + && usage_array[i].usage[0] != NOUSAGE_STR[0] + ) { + printf("=item B<%s>\n\n", usage_array[i].aname); + if (usage_array[i].usage[0]) + printf("%s %s\n\n", usage_array[i].aname, usage_array[i].usage); + else + printf("%s\n\n", usage_array[i].aname); + } + } + printf("=back\n\n"); + + return 0; +} + +/* TODO: we used to make options bold with B<> and output an example too: + +=item B + +cat [B<-u>] [FILE]... + +Concatenate FILE(s) and print them to stdout + +Options: + -u Use unbuffered i/o (ignored) + +Example: + $ cat /proc/uptime + 110716.72 17.67 + +*/ diff --git a/busybox/applets_sh/README b/busybox/applets_sh/README new file mode 100644 index 00000000000000..9dcd38ae380ffb --- /dev/null +++ b/busybox/applets_sh/README @@ -0,0 +1,5 @@ +This directory contains examples of applets implemented as shell scripts. + +So far these scripts are not hooked to the build system and are not +installed by "make install". If you want to use them, +you need to install them by hand. diff --git a/busybox/applets_sh/dos2unix b/busybox/applets_sh/dos2unix new file mode 100644 index 00000000000000..0fd5206f640057 --- /dev/null +++ b/busybox/applets_sh/dos2unix @@ -0,0 +1,5 @@ +#!/bin/sh +# TODO: use getopt to avoid parsing options as filenames, +# and to support -- and --help +[ $# -ne 0 ] && DASH_I=-i +sed $DASH_I -e 's/\r$//' "$@" diff --git a/busybox/applets_sh/nologin b/busybox/applets_sh/nologin new file mode 100644 index 00000000000000..3768eaaa7e699c --- /dev/null +++ b/busybox/applets_sh/nologin @@ -0,0 +1,4 @@ +#!/bin/sh +cat /etc/nologin.txt 2>/dev/null || echo "This account is not available" +sleep 5 +exit 1 diff --git a/busybox/applets_sh/tac b/busybox/applets_sh/tac new file mode 100644 index 00000000000000..c5a8e39c11152d --- /dev/null +++ b/busybox/applets_sh/tac @@ -0,0 +1,7 @@ +#!/bin/sh +# TODO: use getopt to avoid parsing options as filenames, +# and to support -- and --help +for i in "$@" +do +sed -e '1!G;h;$!d' "$i" +done diff --git a/busybox/applets_sh/unix2dos b/busybox/applets_sh/unix2dos new file mode 100644 index 00000000000000..70e04290616b2f --- /dev/null +++ b/busybox/applets_sh/unix2dos @@ -0,0 +1,5 @@ +#!/bin/sh +# TODO: use getopt to avoid parsing options as filenames, +# and to support -- and --help +[ $# -ne 0 ] && DASH_I=-i +sed $DASH_I -e 's/$/\r/' "$@" diff --git a/busybox/arch/i386/Makefile b/busybox/arch/i386/Makefile new file mode 100644 index 00000000000000..425361fd9b51ee --- /dev/null +++ b/busybox/arch/i386/Makefile @@ -0,0 +1,13 @@ +# ========================================================================== +# Build system +# ========================================================================== + +# Allow i486 insns (basically, bswap insn) +# Do not try to tune for 486+ (might add padding) +CFLAGS += $(call cc-option,-march=i486 -mtune=i386,) + +ifeq ($(CONFIG_STACK_OPTIMIZATION_386),y) +# -mpreferred-stack-boundary=2 is essential in preventing gcc 4.2.x +# from aligning stack to 16 bytes. (Which is gcc's way of supporting SSE). +CFLAGS += $(call cc-option,-mpreferred-stack-boundary=2,) +endif diff --git a/busybox/arch/sparc/Makefile b/busybox/arch/sparc/Makefile new file mode 100644 index 00000000000000..0b1c56cb5eb89f --- /dev/null +++ b/busybox/arch/sparc/Makefile @@ -0,0 +1,11 @@ +# When building a library, even intra-library references, +# such as from find_applet_by_name() to applet_names[], +# don't work with -fpic on sparc, needs -fPIC. +# Don't know why it fails in this case but works when +# a binary is being built. +# +# (if is superfluous, ARCH_FPIC is only used by library build, but it +# demonstrates the point: non-pic binary does not need it) +ifeq ($(CONFIG_BUILD_LIBBUSYBOX),y) +ARCH_FPIC = -fPIC +endif diff --git a/busybox/arch/sparc64/Makefile b/busybox/arch/sparc64/Makefile new file mode 100644 index 00000000000000..0b1c56cb5eb89f --- /dev/null +++ b/busybox/arch/sparc64/Makefile @@ -0,0 +1,11 @@ +# When building a library, even intra-library references, +# such as from find_applet_by_name() to applet_names[], +# don't work with -fpic on sparc, needs -fPIC. +# Don't know why it fails in this case but works when +# a binary is being built. +# +# (if is superfluous, ARCH_FPIC is only used by library build, but it +# demonstrates the point: non-pic binary does not need it) +ifeq ($(CONFIG_BUILD_LIBBUSYBOX),y) +ARCH_FPIC = -fPIC +endif diff --git a/busybox/archival/Config.src b/busybox/archival/Config.src new file mode 100644 index 00000000000000..6f4f30c43d86fd --- /dev/null +++ b/busybox/archival/Config.src @@ -0,0 +1,38 @@ +# +# For a description of the syntax of this configuration file, +# see docs/Kconfig-language.txt. +# + +menu "Archival Utilities" + +config FEATURE_SEAMLESS_XZ + bool "Make tar, rpm, modprobe etc understand .xz data" + default y + +config FEATURE_SEAMLESS_LZMA + bool "Make tar, rpm, modprobe etc understand .lzma data" + default y + +config FEATURE_SEAMLESS_BZ2 + bool "Make tar, rpm, modprobe etc understand .bz2 data" + default y + +config FEATURE_SEAMLESS_GZ + bool "Make tar, rpm, modprobe etc understand .gz data" + default y + +config FEATURE_SEAMLESS_Z + bool "Make tar, rpm, modprobe etc understand .Z data" + default n # it is ancient + +INSERT + +config FEATURE_LZMA_FAST + bool "Optimize lzma for speed" + default n + depends on UNLZMA || LZCAT || LZMA || FEATURE_SEAMLESS_LZMA + help + This option reduces decompression time by about 25% at the cost of + a 1K bigger binary. + +endmenu diff --git a/busybox/archival/Kbuild.src b/busybox/archival/Kbuild.src new file mode 100644 index 00000000000000..b3a7d538f4a2c0 --- /dev/null +++ b/busybox/archival/Kbuild.src @@ -0,0 +1,11 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +libs-y += libarchive/ + +lib-y:= + +INSERT diff --git a/busybox/archival/ar.c b/busybox/archival/ar.c new file mode 100644 index 00000000000000..f4edeb087c2cf7 --- /dev/null +++ b/busybox/archival/ar.c @@ -0,0 +1,297 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ar implementation for busybox + * + * Copyright (C) 2000 by Glenn McGrath + * + * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Archive creation support: + * Copyright (C) 2010 Nokia Corporation. All rights reserved. + * Written by Alexander Shishkin. + * + * There is no single standard to adhere to so ar may not portable + * between different systems + * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html + */ +//config:config AR +//config: bool "ar (9.5 kb)" +//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. In practice, it is used exclusively +//config: for object module archives used by compilers. +//config: +//config: Unless you have a specific application which requires ar, you should +//config: probably say N here: most compilers come with their own ar utility. +//config: +//config:config FEATURE_AR_LONG_FILENAMES +//config: bool "Support long filenames (not needed for debs)" +//config: default y +//config: depends on AR +//config: help +//config: By default the ar format can only store the first 15 characters +//config: of the filename, this option removes that limitation. +//config: It supports the GNU ar long filename method which moves multiple long +//config: filenames into a the data section of a new ar entry. +//config: +//config:config FEATURE_AR_CREATE +//config: bool "Support archive creation" +//config: default y +//config: depends on AR +//config: help +//config: This enables archive creation (-c and -r) with busybox ar. + +//applet:IF_AR(APPLET(ar, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_AR) += ar.o + +//usage:#define ar_trivial_usage +//usage: "[-o] [-v] [-p] [-t] [-x] ARCHIVE FILES" +//usage:#define ar_full_usage "\n\n" +//usage: "Extract or list FILES from an ar archive\n" +//usage: "\n -o Preserve original dates" +//usage: "\n -p Extract to stdout" +//usage: "\n -t List" +//usage: "\n -x Extract" +//usage: "\n -v Verbose" + +#include "libbb.h" +#include "bb_archive.h" +#include "ar.h" + +#if ENABLE_FEATURE_AR_CREATE +/* filter out entries with same names as specified on the command line */ +static char FAST_FUNC filter_replaceable(archive_handle_t *handle) +{ + if (find_list_entry(handle->accept, handle->file_header->name)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +static void output_ar_header(archive_handle_t *handle) +{ + /* GNU ar 2.19.51.0.14 creates malformed archives + * if input files are >10G. It also truncates files >4GB + * (uses "size mod 4G"). We abort in this case: + * We could add support for up to 10G files, but this is unlikely to be useful. + * Note that unpacking side limits all fields to "unsigned int" data type, + * and treats "all ones" as an error indicator. Thus max we allow here is UINT_MAX-1. + */ + enum { + /* for 2nd field: mtime */ + MAX11CHARS = UINT_MAX > 0xffffffff ? (unsigned)99999999999 : UINT_MAX-1, + /* for last field: filesize */ + MAX10CHARS = UINT_MAX > 0xffffffff ? (unsigned)9999999999 : UINT_MAX-1, + }; + + struct file_header_t *fh = handle->file_header; + + if (handle->offset & 1) { + xwrite(handle->src_fd, "\n", 1); + handle->offset++; + } + + /* Careful! The widths should be exact. Fields must be separated */ + if (sizeof(off_t) > 4 && fh->size > (off_t)MAX10CHARS) { + bb_error_msg_and_die("'%s' is bigger than ar can handle", fh->name); + } + fdprintf(handle->src_fd, "%-16.16s%-12lu%-6u%-6u%-8o%-10"OFF_FMT"u`\n", + fh->name, + (sizeof(time_t) > 4 && fh->mtime > MAX11CHARS) ? (long)0 : (long)fh->mtime, + fh->uid > 99999 ? 0 : (int)fh->uid, + fh->gid > 99999 ? 0 : (int)fh->gid, + (int)fh->mode & 07777777, + fh->size + ); + + handle->offset += AR_HEADER_LEN; +} + +/* + * when replacing files in an existing archive, copy from the + * original archive those files that are to be left intact + */ +static void FAST_FUNC copy_data(archive_handle_t *handle) +{ + archive_handle_t *out_handle = handle->ar__out; + struct file_header_t *fh = handle->file_header; + + out_handle->file_header = fh; + output_ar_header(out_handle); + + bb_copyfd_exact_size(handle->src_fd, out_handle->src_fd, fh->size); + out_handle->offset += fh->size; +} + +static int write_ar_header(archive_handle_t *handle) +{ + char *fn; + char fn_h[17]; /* 15 + "/" + NUL */ + struct stat st; + int fd; + + fn = llist_pop(&handle->accept); + if (!fn) + return -1; + + xstat(fn, &st); + + handle->file_header->mtime = st.st_mtime; + handle->file_header->uid = st.st_uid; + handle->file_header->gid = st.st_gid; + handle->file_header->mode = st.st_mode; + handle->file_header->size = st.st_size; + handle->file_header->name = fn_h; +//TODO: if ENABLE_FEATURE_AR_LONG_FILENAMES... + sprintf(fn_h, "%.15s/", bb_basename(fn)); + + output_ar_header(handle); + + fd = xopen(fn, O_RDONLY); + bb_copyfd_exact_size(fd, handle->src_fd, st.st_size); + close(fd); + handle->offset += st.st_size; + + return 0; +} + +static int write_ar_archive(archive_handle_t *handle) +{ + struct stat st; + archive_handle_t *out_handle; + + xfstat(handle->src_fd, &st, handle->ar__name); + + /* if archive exists, create a new handle for output. + * we create it in place of the old one. + */ + if (st.st_size != 0) { + out_handle = init_handle(); + xunlink(handle->ar__name); + out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC); + out_handle->accept = handle->accept; + } else { + out_handle = handle; + } + + handle->ar__out = out_handle; + + xwrite(out_handle->src_fd, AR_MAGIC "\n", AR_MAGIC_LEN + 1); + out_handle->offset += AR_MAGIC_LEN + 1; + + /* skip to the end of the archive if we have to append stuff */ + if (st.st_size != 0) { + handle->filter = filter_replaceable; + handle->action_data = copy_data; + unpack_ar_archive(handle); + } + + while (write_ar_header(out_handle) == 0) + continue; + + /* optional, since we exit right after we return */ + if (ENABLE_FEATURE_CLEAN_UP) { + close(handle->src_fd); + if (out_handle->src_fd != handle->src_fd) + close(out_handle->src_fd); + } + + return EXIT_SUCCESS; +} +#endif /* FEATURE_AR_CREATE */ + +static void FAST_FUNC header_verbose_list_ar(const file_header_t *file_header) +{ + const char *mode = bb_mode_string(file_header->mode); + char *mtime; + + mtime = ctime(&file_header->mtime); + mtime[16] = ' '; + memmove(&mtime[17], &mtime[20], 4); + mtime[21] = '\0'; + printf("%s %u/%u%7"OFF_FMT"u %s %s\n", &mode[1], + (int)file_header->uid, (int)file_header->gid, + file_header->size, + &mtime[4], file_header->name + ); +} + +#define AR_OPT_VERBOSE (1 << 0) +#define AR_OPT_PRESERVE_DATE (1 << 1) +/* "ar r" implies create, but warns about it. c suppresses warning. + * bbox accepts but ignores it: */ +#define AR_OPT_CREATE (1 << 2) + +#define AR_CMD_PRINT (1 << 3) +#define FIRST_CMD AR_CMD_PRINT +#define AR_CMD_LIST (1 << 4) +#define AR_CMD_EXTRACT (1 << 5) +#define AR_CMD_INSERT (1 << 6) + +int ar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ar_main(int argc UNUSED_PARAM, char **argv) +{ + archive_handle_t *archive_handle; + unsigned opt, t; + + archive_handle = init_handle(); + + /* prepend '-' to the first argument if required */ + if (argv[1] && argv[1][0] != '-' && argv[1][0] != '\0') + argv[1] = xasprintf("-%s", argv[1]); + opt = getopt32(argv, "^" + "voc""ptx"IF_FEATURE_AR_CREATE("r") + "\0" + /* -1: at least one arg is reqd */ + /* one of p,t,x[,r] is required */ + "-1:p:t:x"IF_FEATURE_AR_CREATE(":r") + ); + argv += optind; + + t = opt / FIRST_CMD; + if (t & (t-1)) /* more than one of p,t,x[,r] are specified */ + bb_show_usage(); + + if (opt & AR_CMD_PRINT) { + archive_handle->action_data = data_extract_to_stdout; + } + if (opt & AR_CMD_LIST) { + archive_handle->action_header = header_list; + } + if (opt & AR_CMD_EXTRACT) { + archive_handle->action_data = data_extract_all; + } + if (opt & AR_OPT_PRESERVE_DATE) { + archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE; + } + if (opt & AR_OPT_VERBOSE) { + archive_handle->action_header = header_verbose_list_ar; + } +#if ENABLE_FEATURE_AR_CREATE + archive_handle->ar__name = *argv; +#endif + archive_handle->src_fd = xopen(*argv++, + (opt & AR_CMD_INSERT) + ? O_RDWR | O_CREAT + : O_RDONLY + ); + + if (*argv) + archive_handle->filter = filter_accept_list; + while (*argv) { + llist_add_to_end(&archive_handle->accept, *argv++); + } + +#if ENABLE_FEATURE_AR_CREATE + if (opt & AR_CMD_INSERT) + return write_ar_archive(archive_handle); +#endif + + unpack_ar_archive(archive_handle); + + return EXIT_SUCCESS; +} diff --git a/busybox/archival/bbunzip.c b/busybox/archival/bbunzip.c new file mode 100644 index 00000000000000..a2ce0a13cd5a3a --- /dev/null +++ b/busybox/archival/bbunzip.c @@ -0,0 +1,597 @@ +/* vi: set sw=4 ts=4: */ +/* + * Common code for gunzip-like applets + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//kbuild:lib-$(CONFIG_ZCAT) += bbunzip.o +//kbuild:lib-$(CONFIG_GUNZIP) += bbunzip.o +//kbuild:lib-$(CONFIG_BZCAT) += bbunzip.o +//kbuild:lib-$(CONFIG_BUNZIP2) += bbunzip.o + +/* 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 + +#include "libbb.h" +#include "bb_archive.h" + +static +int open_to_or_warn(int to_fd, const char *filename, int flags, int mode) +{ + int fd = open3_or_warn(filename, flags, mode); + if (fd < 0) { + return 1; + } + xmove_fd(fd, to_fd); + return 0; +} + +char* FAST_FUNC append_ext(char *filename, const char *expected_ext) +{ + return xasprintf("%s.%s", filename, expected_ext); +} + +int FAST_FUNC bbunpack(char **argv, + 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 +) +{ + struct stat stat_buf; + IF_DESKTOP(long long) int status = 0; + char *filename, *new_name; + smallint exitcode = 0; + transformer_state_t xstate; + + do { + /* NB: new_name is *maybe* malloc'ed! */ + new_name = NULL; + filename = *argv; /* can be NULL - 'streaming' bunzip2 */ + + if (filename && LONE_DASH(filename)) + filename = NULL; + + /* Open src */ + if (filename) { + if (!(option_mask32 & BBUNPK_SEAMLESS_MAGIC)) { + if (stat(filename, &stat_buf) != 0) { + err_name: + bb_simple_perror_msg(filename); + err: + exitcode = 1; + goto free_name; + } + if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0)) + goto err; + } else { + /* "clever zcat" with FILE */ + /* fail_if_not_compressed because zcat refuses uncompressed input */ + int fd = open_zipped(filename, /*fail_if_not_compressed:*/ 1); + if (fd < 0) + goto err_name; + xmove_fd(fd, STDIN_FILENO); + } + } else + if (option_mask32 & BBUNPK_SEAMLESS_MAGIC) { + /* "clever zcat" on stdin */ + if (setup_unzip_on_fd(STDIN_FILENO, /*fail_if_not_compressed*/ 1)) + goto err; + } + + /* Special cases: test, stdout */ + if (option_mask32 & (BBUNPK_OPT_STDOUT|BBUNPK_OPT_TEST)) { + if (option_mask32 & BBUNPK_OPT_TEST) + if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0)) + xfunc_die(); + filename = NULL; + } + + /* Open dst if we are going to unpack to file */ + if (filename) { + new_name = make_new_name(filename, expected_ext); + if (!new_name) { + bb_error_msg("%s: unknown suffix - ignored", filename); + goto err; + } + + /* -f: overwrite existing output files */ + if (option_mask32 & BBUNPK_OPT_FORCE) { + unlink(new_name); + } + + /* O_EXCL: "real" bunzip2 doesn't overwrite files */ + /* GNU gunzip does not bail out, but goes to next file */ + if (open_to_or_warn(STDOUT_FILENO, new_name, O_WRONLY | O_CREAT | O_EXCL, + stat_buf.st_mode)) + goto err; + } + + /* Check that the input is sane */ + if (!(option_mask32 & BBUNPK_OPT_FORCE) && isatty(STDIN_FILENO)) { + bb_error_msg_and_die("compressed data not read from terminal, " + "use -f to force it"); + } + + if (!(option_mask32 & BBUNPK_SEAMLESS_MAGIC)) { + init_transformer_state(&xstate); + /*xstate.signature_skipped = 0; - already is */ + /*xstate.src_fd = STDIN_FILENO; - already is */ + xstate.dst_fd = STDOUT_FILENO; + status = unpacker(&xstate); + if (status < 0) + exitcode = 1; + } else { + if (bb_copyfd_eof(STDIN_FILENO, STDOUT_FILENO) < 0) + /* Disk full, tty closed, etc. No point in continuing */ + xfunc_die(); + } + + if (!(option_mask32 & BBUNPK_OPT_STDOUT)) + xclose(STDOUT_FILENO); /* with error check! */ + + if (filename) { + char *del = new_name; + + if (status >= 0) { + unsigned new_name_len; + + /* TODO: restore other things? */ + if (xstate.mtime != 0) { + struct timeval times[2]; + + 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 + * then closing resets the mtime + * back to current time. */ + utimes(new_name, times); /* ignoring errors */ + } + + if (ENABLE_DESKTOP) + new_name_len = strlen(new_name); + /* Restore source filename (unless tgz -> tar case) */ + if (new_name == filename) { + new_name_len = strlen(filename); + filename[new_name_len] = '.'; + } + /* Extreme bloat for gunzip compat */ + /* Some users do want this info... */ + if (ENABLE_DESKTOP && (option_mask32 & BBUNPK_OPT_VERBOSE)) { + unsigned percent = status + ? ((uoff_t)stat_buf.st_size * 100u / (unsigned long long)status) + : 0; + fprintf(stderr, "%s: %u%% - replaced with %.*s\n", + filename, + 100u - percent, + new_name_len, new_name + ); + } + /* Delete _source_ file */ + del = filename; + if (option_mask32 & BBUNPK_OPT_KEEP) /* ... unless -k */ + del = NULL; + } + if (del) + xunlink(del); + free_name: + if (new_name != filename) + free(new_name); + } + } while (*argv && *++argv); + + if (option_mask32 & BBUNPK_OPT_STDOUT) + xclose(STDOUT_FILENO); /* with error check! */ + + return exitcode; +} + +#if ENABLE_UNCOMPRESS \ + || ENABLE_FEATURE_BZIP2_DECOMPRESS \ + || 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) +{ + char *extension = strrchr(filename, '.'); + if (!extension || strcmp(extension + 1, expected_ext) != 0) { + /* Mimic GNU gunzip - "real" bunzip2 tries to */ + /* unpack file anyway, to file.out */ + return NULL; + } + *extension = '\0'; + return filename; +} +#endif + + +/* + * Uncompress applet for busybox (c) 2002 Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//usage:#define uncompress_trivial_usage +//usage: "[-cf] [FILE]..." +//usage:#define uncompress_full_usage "\n\n" +//usage: "Decompress .Z file[s]\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Overwrite" + +//config:config UNCOMPRESS +//config: bool "uncompress (7.1 kb)" +//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. + +//applet:IF_UNCOMPRESS(APPLET(uncompress, BB_DIR_BIN, BB_SUID_DROP)) +//kbuild:lib-$(CONFIG_UNCOMPRESS) += bbunzip.o +#if ENABLE_UNCOMPRESS +int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uncompress_main(int argc UNUSED_PARAM, char **argv) +{ +// (N)compress 4.2.4.4: +// -d If given, decompression is done instead +// -c Write output on stdout, don't remove original +// -b Parameter limits the max number of bits/code +// -f Forces output file to be generated +// -v Write compression statistics +// -V Output vesion and compile options +// -r Recursive. If a filename is a directory, descend into it and compress everything + getopt32(argv, "cf"); + + argv += optind; + + return bbunpack(argv, unpack_Z_stream, make_new_name_generic, "Z"); +} +#endif + + +/* + * Gzip implementation for busybox + * + * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly. + * + * Originally adjusted for busybox by Sven Rudolph + * based on gzip sources + * + * Adjusted further by Erik Andersen to support files as + * well as stdin/stdout, and to generally behave itself wrt command line + * handling. + * + * General cleanup to better adhere to the style guide and make use of standard + * busybox functions by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface + * Copyright (C) 1992-1993 Jean-loup Gailly + * The unzip code was written and put in the public domain by Mark Adler. + * Portions of the lzw code are derived from the public domain 'compress' + * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, + * Ken Turkowski, Dave Mack and Peter Jannesen. + */ +//usage:#define gunzip_trivial_usage +//usage: "[-cfkt] [FILE]..." +//usage:#define gunzip_full_usage "\n\n" +//usage: "Decompress FILEs (or stdin)\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -k Keep input files" +//usage: "\n -t Test file integrity" +//usage: +//usage:#define gunzip_example_usage +//usage: "$ ls -la /tmp/BusyBox*\n" +//usage: "-rw-rw-r-- 1 andersen andersen 557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n" +//usage: "$ gunzip /tmp/BusyBox-0.43.tar.gz\n" +//usage: "$ ls -la /tmp/BusyBox*\n" +//usage: "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n" +//usage: +//usage:#define zcat_trivial_usage +//usage: "[FILE]..." +//usage:#define zcat_full_usage "\n\n" +//usage: "Decompress to stdout" + +//config:config GUNZIP +//config: bool "gunzip (12 kb)" +//config: default y +//config: select FEATURE_GZIP_DECOMPRESS +//config: help +//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 (25 kb)" +//config: default y +//config: select FEATURE_GZIP_DECOMPRESS +//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 + +//applet:IF_GUNZIP(APPLET(gunzip, BB_DIR_BIN, BB_SUID_DROP)) +// APPLET_ODDNAME:name main location suid_type help +//applet:IF_ZCAT(APPLET_ODDNAME(zcat, gunzip, BB_DIR_BIN, BB_SUID_DROP, zcat)) +#if ENABLE_FEATURE_GZIP_DECOMPRESS +static +char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM) +{ + char *extension = strrchr(filename, '.'); + + if (!extension) + return NULL; + + extension++; + if (strcmp(extension, "tgz" + 1) == 0 +#if ENABLE_FEATURE_SEAMLESS_Z + || (extension[0] == 'Z' && extension[1] == '\0') +#endif + ) { + extension[-1] = '\0'; + } else if (strcmp(extension, "tgz") == 0) { + filename = xstrdup(filename); + extension = strrchr(filename, '.'); + extension[2] = 'a'; + extension[3] = 'r'; + } else { + return NULL; + } + return filename; +} + +#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: + * -n --no-name + * gzip: do not save the original file name and time stamp. + * (The original name is always saved if the name had to be truncated.) + * gunzip: do not restore the original file name/time even if present + * (remove only the gzip suffix from the compressed file name). + * This option is the default when decompressing. + * -N --name + * gzip: always save the original file name and time stamp (this is the default) + * gunzip: restore the original file name and time stamp if present. + */ +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 + getopt32long(argv, BBUNPK_OPTSTR "dtn", gunzip_longopts); +#else + getopt32(argv, BBUNPK_OPTSTR "dtn"); +#endif + argv += optind; + + /* If called as zcat... + * Normally, "zcat" is just "gunzip -c". + * But if seamless magic is enabled, then we are much more clever. + */ + if (ENABLE_ZCAT && (!ENABLE_GUNZIP || applet_name[1] == 'c')) + option_mask32 |= BBUNPK_OPT_STDOUT | BBUNPK_SEAMLESS_MAGIC; + + return bbunpack(argv, unpack_gz_stream, make_new_name_gunzip, /*unused:*/ NULL); +} +#endif /* FEATURE_GZIP_DECOMPRESS */ + + +/* + * Modified for busybox by Glenn McGrath + * Added support output to stdout by Thomas Lundquist + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//usage:#define bunzip2_trivial_usage +//usage: "[-cfk] [FILE]..." +//usage:#define bunzip2_full_usage "\n\n" +//usage: "Decompress FILEs (or stdin)\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -k Keep input files" +//usage:#define bzcat_trivial_usage +//usage: "[FILE]..." +//usage:#define bzcat_full_usage "\n\n" +//usage: "Decompress to stdout" + +//config:config BUNZIP2 +//config: bool "bunzip2 (8.8 kb)" +//config: default y +//config: select FEATURE_BZIP2_DECOMPRESS +//config: help +//config: bunzip2 is a compression utility using the Burrows-Wheeler block +//config: sorting text compression algorithm, and Huffman coding. Compression +//config: is generally considerably better than that achieved by more +//config: conventional LZ77/LZ78-based compressors, and approaches the +//config: performance of the PPM family of statistical compressors. +//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 (8.8 kb)" +//config: default y +//config: select FEATURE_BZIP2_DECOMPRESS +//config: help +//config: Alias to "bunzip2 -c". + +//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP)) +// APPLET_ODDNAME:name main location suid_type help +//applet:IF_BZCAT(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat)) +#if ENABLE_FEATURE_BZIP2_DECOMPRESS || 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, BBUNPK_OPTSTR "dt"); + argv += optind; + if (ENABLE_BZCAT && (!ENABLE_BUNZIP2 || applet_name[2] == 'c')) /* bzcat */ + option_mask32 |= BBUNPK_OPT_STDOUT; + + return bbunpack(argv, unpack_bz2_stream, make_new_name_generic, "bz2"); +} +#endif + + +/* + * Small lzma deflate implementation. + * Copyright (C) 2006 Aurelien Jacobs + * + * Based on bunzip.c from busybox + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//usage:#define unlzma_trivial_usage +//usage: "[-cfk] [FILE]..." +//usage:#define unlzma_full_usage "\n\n" +//usage: "Decompress FILE (or stdin)\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -k Keep input files" +//usage: +//usage:#define lzma_trivial_usage +//usage: "-d [-cfk] [FILE]..." +//usage:#define lzma_full_usage "\n\n" +//usage: "Decompress FILE (or stdin)\n" +//usage: "\n -d Decompress" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -k Keep input files" +//usage: +//usage:#define lzcat_trivial_usage +//usage: "[FILE]..." +//usage:#define lzcat_full_usage "\n\n" +//usage: "Decompress to stdout" + +//config:config UNLZMA +//config: bool "unlzma (8.6 kb)" +//config: default y +//config: help +//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:config LZCAT +//config: bool "lzcat (8.5 kb)" +//config: default y +//config: help +//config: Alias to "unlzma -c". +//config: +//config:config LZMA +//config: bool "lzma -d" +//config: default y +//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. + +//applet:IF_UNLZMA(APPLET(unlzma, BB_DIR_USR_BIN, BB_SUID_DROP)) +// APPLET_ODDNAME:name main location suid_type help +//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 +//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) +{ + IF_LZMA(int opts =) getopt32(argv, BBUNPK_OPTSTR "dt"); +# if ENABLE_LZMA + /* lzma without -d or -t? */ + if (applet_name[2] == 'm' && !(opts & (BBUNPK_OPT_DECOMPRESS|BBUNPK_OPT_TEST))) + bb_show_usage(); +# endif + /* lzcat? */ + if (ENABLE_LZCAT && applet_name[2] == 'c') + option_mask32 |= BBUNPK_OPT_STDOUT; + + argv += optind; + return bbunpack(argv, unpack_lzma_stream, make_new_name_generic, "lzma"); +} +#endif + + +//usage:#define unxz_trivial_usage +//usage: "[-cfk] [FILE]..." +//usage:#define unxz_full_usage "\n\n" +//usage: "Decompress FILE (or stdin)\n" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -k Keep input files" +//usage: +//usage:#define xz_trivial_usage +//usage: "-d [-cfk] [FILE]..." +//usage:#define xz_full_usage "\n\n" +//usage: "Decompress FILE (or stdin)\n" +//usage: "\n -d Decompress" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -k Keep input files" +//usage: +//usage:#define xzcat_trivial_usage +//usage: "[FILE]..." +//usage:#define xzcat_full_usage "\n\n" +//usage: "Decompress to stdout" + +//config:config UNXZ +//config: bool "unxz (13 kb)" +//config: default y +//config: help +//config: unxz is a unlzma successor. +//config: +//config:config XZCAT +//config: bool "xzcat (13 kb)" +//config: default y +//config: help +//config: Alias to "unxz -c". +//config: +//config:config XZ +//config: bool "xz -d" +//config: default y +//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_ODDNAME:name main location suid_type help +//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 +//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) +{ + IF_XZ(int opts =) getopt32(argv, BBUNPK_OPTSTR "dt"); +# if ENABLE_XZ + /* xz without -d or -t? */ + if (applet_name[2] == '\0' && !(opts & (BBUNPK_OPT_DECOMPRESS|BBUNPK_OPT_TEST))) + bb_show_usage(); +# endif + /* xzcat? */ + if (ENABLE_XZCAT && applet_name[2] == 'c') + option_mask32 |= BBUNPK_OPT_STDOUT; + + argv += optind; + return bbunpack(argv, unpack_xz_stream, make_new_name_generic, "xz"); +} +#endif diff --git a/busybox/archival/bbunzip_test.sh b/busybox/archival/bbunzip_test.sh new file mode 100644 index 00000000000000..b8e31bf9737d17 --- /dev/null +++ b/busybox/archival/bbunzip_test.sh @@ -0,0 +1,61 @@ +#!/bin/sh +# Test that concatenated gz files are unpacking correctly. +# It also tests that unpacking in general is working right. +# Since zip code has many corner cases, run it for a few hours +# to get a decent coverage (200000 tests or more). + +gzip="gzip" +gunzip="../busybox gunzip" +# Or the other way around: +#gzip="../busybox gzip" +#gunzip="gunzip" + +c=0 +i=$PID +while true; do + c=$((c+1)) + + # RANDOM is not very random on some shells. Spice it up. + # 100003 is prime + len1=$(( (((RANDOM*RANDOM)^i) & 0x7ffffff) % 100003 )) + i=$((i * 1664525 + 1013904223)) + len2=$(( (((RANDOM*RANDOM)^i) & 0x7ffffff) % 100003 )) + + # Just using urandom will make gzip use method 0 (store) - + # not good for test coverage! + cat /dev/urandom | while true; do read junk; echo "junk $c $i $junk"; done \ + | dd bs=$len1 count=1 >z1 2>/dev/null + cat /dev/urandom | while true; do read junk; echo "junk $c $i $junk"; done \ + | dd bs=$len2 count=1 >z2 2>/dev/null + + $gzip zz.gz + $gzip >zz.gz + $gunzip -c zz.gz >z9 || { + echo "Exitcode $?" + exit + } + sum=`cat z1 z2 | md5sum` + sum9=`md5sum /dev/null + $gzip z1 + $gzip z2 + cat z1.gz z2.gz z1.gz z2.gz >zz.gz + $gunzip -c zz.gz >z9 || { + echo "Exitcode $? (2)" + exit + } + sum9=`md5sum /dev/null diff --git a/busybox/archival/bbunzip_test3.sh b/busybox/archival/bbunzip_test3.sh new file mode 100644 index 00000000000000..2dc4afda184417 --- /dev/null +++ b/busybox/archival/bbunzip_test3.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# Leak test for gunzip. Watch top for growing process size. +# In this case we look for leaks in "concatenated .gz" code - +# we feed gunzip with a stream of .gz files. + +i=$PID +c=0 +while true; do + c=$((c + 1)) + echo "Block# $c" >&2 + # RANDOM is not very random on some shells. Spice it up. + i=$((i * 1664525 + 1013904223)) + # 100003 is prime + len=$(( (((RANDOM*RANDOM)^i) & 0x7ffffff) % 100003 )) + + # Just using urandom will make gzip use method 0 (store) - + # not good for test coverage! + cat /dev/urandom \ + | while true; do read junk; echo "junk $c $i $junk"; done \ + | dd bs=$len count=1 2>/dev/null \ + | gzip >xxx.gz + cat xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz +done | ../busybox gunzip -c >/dev/null diff --git a/busybox/archival/bzip2.c b/busybox/archival/bzip2.c new file mode 100644 index 00000000000000..357891ca3d0946 --- /dev/null +++ b/busybox/archival/bzip2.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2007 Denys Vlasenko + * + * This file uses bzip2 library code which is written + * by Julian Seward . + * See README and LICENSE files in bz/ directory for more information + * about bzip2 library code. + */ +//config:config BZIP2 +//config: bool "bzip2 (18 kb)" +//config: default y +//config: help +//config: bzip2 is a compression utility using the Burrows-Wheeler block +//config: sorting text compression algorithm, and Huffman coding. Compression +//config: is generally considerably better than that achieved by more +//config: conventional LZ77/LZ78-based compressors, and approaches the +//config: performance of the PPM family of statistical compressors. +//config: +//config: Unless you have a specific application which requires bzip2, you +//config: should probably say N here. +//config: +//config:config BZIP2_SMALL +//config: int "Trade bytes for speed (0:fast, 9:small)" +//config: default 8 # all "fast or small" options default to small +//config: range 0 9 +//config: depends on BZIP2 +//config: help +//config: Trade code size versus speed. +//config: Approximate values with gcc-6.3.0 "bzip -9" compressing +//config: linux-4.15.tar were: +//config: value time (sec) code size (386) +//config: 9 (smallest) 70.11 7687 +//config: 8 67.93 8091 +//config: 7 67.88 8405 +//config: 6 67.78 8624 +//config: 5 67.05 9427 +//config: 4-0 (fastest) 64.14 12083 +//config: +//config:config FEATURE_BZIP2_DECOMPRESS +//config: bool "Enable decompression" +//config: default y +//config: depends on BZIP2 || BUNZIP2 || BZCAT +//config: help +//config: Enable -d (--decompress) and -t (--test) options for bzip2. +//config: This will be automatically selected if bunzip2 or bzcat is +//config: enabled. + +//applet:IF_BZIP2(APPLET(bzip2, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_BZIP2) += bzip2.o + +//usage:#define bzip2_trivial_usage +//usage: "[OPTIONS] [FILE]..." +//usage:#define bzip2_full_usage "\n\n" +//usage: "Compress FILEs (or stdin) with bzip2 algorithm\n" +//usage: "\n -1..9 Compression level" +//usage: IF_FEATURE_BZIP2_DECOMPRESS( +//usage: "\n -d Decompress" +//usage: "\n -t Test file integrity" +//usage: ) +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -k Keep input files" + +#include "libbb.h" +#include "bb_archive.h" + +#if CONFIG_BZIP2_SMALL >= 4 +#define BZIP2_SPEED (9 - CONFIG_BZIP2_SMALL) +#else +#define BZIP2_SPEED 5 +#endif + +/* Speed test: + * Compiled with gcc 4.2.1, run on Athlon 64 1800 MHz (512K L2 cache). + * Stock bzip2 is 26.4% slower than bbox bzip2 at SPEED 1 + * (time to compress gcc-4.2.1.tar is 126.4% compared to bbox). + * At SPEED 5 difference is 32.7%. + * + * Test run of all BZIP2_SPEED values on a 11Mb text file: + * Size Time (3 runs) + * 0: 10828 4.145 4.146 4.148 + * 1: 11097 3.845 3.860 3.861 + * 2: 11392 3.763 3.767 3.768 + * 3: 11892 3.722 3.724 3.727 + * 4: 12740 3.637 3.640 3.644 + * 5: 17273 3.497 3.509 3.509 + */ + + +#define BZ_DEBUG 0 +/* Takes ~300 bytes, detects corruption caused by bad RAM etc */ +#define BZ_LIGHT_DEBUG 0 + +#include "libarchive/bz/bzlib.h" + +#include "libarchive/bz/bzlib_private.h" + +#include "libarchive/bz/blocksort.c" +#include "libarchive/bz/bzlib.c" +#include "libarchive/bz/compress.c" +#include "libarchive/bz/huffman.c" + +/* No point in being shy and having very small buffer here. + * bzip2 internal buffers are much bigger anyway, hundreds of kbytes. + * If iobuf is several pages long, malloc() may use mmap, + * making iobuf page aligned and thus (maybe) have one memcpy less + * if kernel is clever enough. + */ +enum { + IOBUF_SIZE = 8 * 1024 +}; + +/* NB: compressStream() has to return -1 on errors, not die. + * bbunpack() will correctly clean up in this case + * (delete incomplete .bz2 file) + */ + +/* Returns: + * -1 on errors + * total written bytes so far otherwise + */ +static +IF_DESKTOP(long long) int bz_write(bz_stream *strm, void* rbuf, ssize_t rlen, void *wbuf) +{ + int n, n2, ret; + + strm->avail_in = rlen; + strm->next_in = rbuf; + while (1) { + strm->avail_out = IOBUF_SIZE; + strm->next_out = wbuf; + + ret = BZ2_bzCompress(strm, rlen ? BZ_RUN : BZ_FINISH); + if (ret != BZ_RUN_OK /* BZ_RUNning */ + && ret != BZ_FINISH_OK /* BZ_FINISHing, but not done yet */ + && ret != BZ_STREAM_END /* BZ_FINISHed */ + ) { + bb_error_msg_and_die("internal error %d", ret); + } + + n = IOBUF_SIZE - strm->avail_out; + if (n) { + n2 = full_write(STDOUT_FILENO, wbuf, n); + if (n2 != n) { + if (n2 >= 0) + errno = 0; /* prevent bogus error message */ + bb_perror_msg(n2 >= 0 ? "short write" : bb_msg_write_error); + return -1; + } + } + + if (ret == BZ_STREAM_END) + break; + if (rlen && strm->avail_in == 0) + break; + } + return 0 IF_DESKTOP( + strm->total_out ); +} + +static +IF_DESKTOP(long long) int FAST_FUNC compressStream(transformer_state_t *xstate UNUSED_PARAM) +{ + IF_DESKTOP(long long) int total; + unsigned opt, level; + ssize_t count; + bz_stream bzs; /* it's small */ +#define strm (&bzs) + char *iobuf; +#define rbuf iobuf +#define wbuf (iobuf + IOBUF_SIZE) + + iobuf = xmalloc(2 * IOBUF_SIZE); + + opt = option_mask32 >> (BBUNPK_OPTSTRLEN IF_FEATURE_BZIP2_DECOMPRESS(+ 2) + 2); + /* skipped BBUNPK_OPTSTR, "dt" and "zs" bits */ + opt |= 0x100; /* if nothing else, assume -9 */ + level = 0; + for (;;) { + level++; + if (opt & 1) break; + opt >>= 1; + } + + BZ2_bzCompressInit(strm, level); + + while (1) { + count = full_read(STDIN_FILENO, rbuf, IOBUF_SIZE); + if (count < 0) { + bb_perror_msg(bb_msg_read_error); + total = -1; + break; + } + /* if count == 0, bz_write finalizes compression */ + total = bz_write(strm, rbuf, count, wbuf); + if (count == 0 || total < 0) + break; + } + + /* Can't be conditional on ENABLE_FEATURE_CLEAN_UP - + * we are called repeatedly + */ + BZ2_bzCompressEnd(strm); + free(iobuf); + + return total; +} + +int bzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int bzip2_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opt; + + /* standard bzip2 flags + * -d --decompress force decompression + * -z --compress force compression + * -k --keep keep (don't delete) input files + * -f --force overwrite existing output files + * -t --test test compressed file integrity + * -c --stdout output to standard out + * -q --quiet suppress noncritical error messages + * -v --verbose be verbose (a 2nd -v gives more) + * -s --small use less memory (at most 2500k) + * -1 .. -9 set block size to 100k .. 900k + * --fast alias for -1 + * --best alias for -9 + */ + + opt = getopt32(argv, "^" + /* Must match BBUNPK_foo constants! */ + BBUNPK_OPTSTR IF_FEATURE_BZIP2_DECOMPRESS("dt") "zs123456789" + "\0" "s2" /* -s means -2 (compatibility) */ + ); +#if ENABLE_FEATURE_BZIP2_DECOMPRESS /* bunzip2_main may not be visible... */ + if (opt & (BBUNPK_OPT_DECOMPRESS|BBUNPK_OPT_TEST)) /* -d and/or -t */ + return bunzip2_main(argc, argv); +#else + /* clear "decompress" and "test" bits (or bbunpack() can get confused) */ + /* in !BZIP2_DECOMPRESS config, these bits are -zs and are unused */ + option_mask32 = opt & ~(BBUNPK_OPT_DECOMPRESS|BBUNPK_OPT_TEST); +#endif + + argv += optind; + return bbunpack(argv, compressStream, append_ext, "bz2"); +} diff --git a/busybox/archival/cpio.c b/busybox/archival/cpio.c new file mode 100644 index 00000000000000..9cacf9de60849a --- /dev/null +++ b/busybox/archival/cpio.c @@ -0,0 +1,523 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cpio implementation for busybox + * + * Copyright (C) 2001 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Limitations: + * Doesn't check CRC's + * Only supports new ASCII and CRC formats + */ +//config:config CPIO +//config: bool "cpio (14 kb)" +//config: default y +//config: help +//config: cpio is an archival utility program used to create, modify, and +//config: extract contents from archives. +//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. +//config: +//config: Unless you have a specific application which requires cpio, you +//config: should probably say N here. +//config: +//config:config FEATURE_CPIO_O +//config: bool "Support archive creation" +//config: default y +//config: depends on CPIO +//config: help +//config: This implementation of cpio can create cpio archives in the "newc" +//config: format only. +//config: +//config:config FEATURE_CPIO_P +//config: bool "Support passthrough mode" +//config: default y +//config: depends on FEATURE_CPIO_O +//config: help +//config: Passthrough mode. Rarely used. + +//applet:IF_CPIO(APPLET(cpio, BB_DIR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_CPIO) += cpio.o + +//usage:#define cpio_trivial_usage +//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 (-i) or list (-t) files from a cpio archive" +//usage: IF_FEATURE_CPIO_O(", or" +//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:" +//usage: "\n -t List" +//usage: "\n -i Extract EXTR_FILEs (or all)" +//usage: IF_FEATURE_CPIO_O( +//usage: "\n -o Create (requires -H newc)" +//usage: ) +//usage: IF_FEATURE_CPIO_P( +//usage: "\n -p DIR Copy files to DIR" +//usage: ) +//usage: "\nOptions:" +//usage: IF_FEATURE_CPIO_O( +//usage: "\n -H newc Archive format" +//usage: ) +//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: "\n -L Dereference symlinks" +//usage: "\n -0 Input is separated by NULs" + +/* GNU cpio 2.9 --help (abridged): + + Modes: + -t, --list List the archive + -i, --extract Extract files from an archive + -o, --create Create the archive + -p, --pass-through Copy-pass mode + + Options valid in any mode: + --block-size=SIZE I/O block size = SIZE * 512 bytes + -B I/O block size = 5120 bytes + -c Use the old portable (ASCII) archive format + -C, --io-size=NUMBER I/O block size in bytes + -f, --nonmatching Only copy files that do not match given pattern + -F, --file=FILE Use FILE instead of standard input or output + -H, --format=FORMAT Use given archive FORMAT + -M, --message=STRING Print STRING when the end of a volume of the + backup media is reached + -n, --numeric-uid-gid If -v, show numeric UID and GID + --quiet Do not print the number of blocks copied + --rsh-command=COMMAND Use remote COMMAND instead of rsh + -v, --verbose Verbosely list the files processed + -V, --dot Print a "." for each file processed + -W, --warning=FLAG Control warning display: 'none','truncate','all'; + multiple options accumulate + + Options valid only in --extract mode: + -b, --swap Swap both halfwords of words and bytes of + halfwords in the data (equivalent to -sS) + -r, --rename Interactively rename files + -s, --swap-bytes Swap the bytes of each halfword in the files + -S, --swap-halfwords Swap the halfwords of each word (4 bytes) + --to-stdout Extract files to standard output + -E, --pattern-file=FILE Read additional patterns specifying filenames to + extract or list from FILE + --only-verify-crc Verify CRC's, don't actually extract the files + + Options valid only in --create mode: + -A, --append Append to an existing archive + -O FILE File to use instead of standard output + + Options valid only in --pass-through mode: + -l, --link Link files instead of copying them, when possible + + Options valid in --extract and --create modes: + --absolute-filenames Do not strip file system prefix components from + the file names + --no-absolute-filenames Create all files relative to the current dir + + Options valid in --create and --pass-through modes: + -0, --null A list of filenames is terminated by a NUL + -a, --reset-access-time Reset the access times of files after reading them + -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][:.][GRP] Set owner of created files + + Options valid in --extract and --pass-through modes: + -d, --make-directories Create leading directories where needed + -m, --preserve-modification-time Retain mtime when creating files + --no-preserve-owner Do not change the ownership of the files + --sparse Write files with blocks of zeros as sparse files + -u, --unconditional Replace all files unconditionally + */ + +#include "libbb.h" +#include "common_bufsiz.h" +#include "bb_archive.h" + +enum { + OPT_EXTRACT = (1 << 0), + OPT_TEST = (1 << 1), + OPT_NUL_TERMINATED = (1 << 2), + OPT_UNCONDITIONAL = (1 << 3), + OPT_VERBOSE = (1 << 4), + OPT_CREATE_LEADING_DIR = (1 << 5), + OPT_PRESERVE_MTIME = (1 << 6), + OPT_DEREF = (1 << 7), + OPT_FILE = (1 << 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,) + IF_LONG_OPTS( OPTBIT_QUIET ,) + IF_LONG_OPTS( OPTBIT_2STDOUT ,) + OPT_CREATE = IF_FEATURE_CPIO_O((1 << OPTBIT_CREATE )) + 0, + OPT_FORMAT = IF_FEATURE_CPIO_O((1 << OPTBIT_FORMAT )) + 0, + OPT_PASSTHROUGH = IF_FEATURE_CPIO_P((1 << OPTBIT_PASSTHROUGH)) + 0, + OPT_QUIET = IF_LONG_OPTS( (1 << OPTBIT_QUIET )) + 0, + OPT_2STDOUT = IF_LONG_OPTS( (1 << OPTBIT_2STDOUT )) + 0, +}; + +#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) +{ + int i; + + i = (- size) & 3; + size += i; + while (--i >= 0) + bb_putchar('\0'); + return size; +} + +/* Return value will become exit code. + * It's ok to exit instead of return. */ +static NOINLINE int cpio_o(void) +{ + struct name_s { + struct name_s *next; + char name[1]; + }; + struct inodes_s { + struct inodes_s *next; + struct name_s *names; + struct stat st; + }; + + struct inodes_s *links = NULL; + off_t bytes = 0; /* output bytes count */ + + while (1) { + const char *name; + char *line; + struct stat st; + + line = (option_mask32 & OPT_NUL_TERMINATED) + ? bb_get_chunk_from_file(stdin, NULL) + : xmalloc_fgetline(stdin); + + if (line) { + /* Strip leading "./[./]..." from the filename */ + name = line; + while (name[0] == '.' && name[1] == '/') { + while (*++name == '/') + continue; + } + if (!*name) { /* line is empty */ + free(line); + continue; + } + if ((option_mask32 & OPT_DEREF) + ? stat(name, &st) + : lstat(name, &st) + ) { + abort_cpio_o: + 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 */ + + /* Store hardlinks for later processing, dont output them */ + if (!S_ISDIR(st.st_mode) && st.st_nlink > 1) { + struct name_s *n; + struct inodes_s *l; + + /* Do we have this hardlink remembered? */ + l = links; + while (1) { + if (l == NULL) { + /* Not found: add new item to "links" list */ + l = xzalloc(sizeof(*l)); + l->st = st; + l->next = links; + links = l; + break; + } + if (l->st.st_ino == st.st_ino) { + /* found */ + break; + } + l = l->next; + } + /* Add new name to "l->names" list */ + n = xmalloc(sizeof(*n) + strlen(name)); + strcpy(n->name, name); + n->next = l->names; + l->names = n; + + free(line); + continue; + } + } else { /* line == NULL: EOF */ + next_link: + if (links) { + /* Output hardlink's data */ + st = links->st; + name = links->names->name; + links->names = links->names->next; + /* GNU cpio is reported to emit file data + * only for the last instance. Mimic that. */ + if (links->names == NULL) + links = links->next; + else + st.st_size = 0; + /* NB: we leak links->names and/or links, + * this is intended (we exit soon anyway) */ + } else { + /* If no (more) hardlinks to output, + * output "trailer" entry */ + 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)); + /* st.st_nlink = 1; - GNU cpio does this */ + } + } + + bytes += printf("070701" + "%08X%08X%08X%08X%08X%08X%08X" + "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */ + /* strlen+1: */ "%08X" + /* chksum: */ "00000000" /* (only for "070702" files) */ + /* name,NUL: */ "%s%c", + (unsigned)(uint32_t) st.st_ino, + (unsigned)(uint32_t) st.st_mode, + (unsigned)(uint32_t) st.st_uid, + (unsigned)(uint32_t) st.st_gid, + (unsigned)(uint32_t) st.st_nlink, + (unsigned)(uint32_t) st.st_mtime, + (unsigned)(uint32_t) st.st_size, + (unsigned)(uint32_t) major(st.st_dev), + (unsigned)(uint32_t) minor(st.st_dev), + (unsigned)(uint32_t) major(st.st_rdev), + (unsigned)(uint32_t) minor(st.st_rdev), + (unsigned)(strlen(name) + 1), + name, '\0'); + bytes = cpio_pad4(bytes); + + if (st.st_size) { + if (S_ISLNK(st.st_mode)) { + char *lpath = xmalloc_readlink_or_warn(name); + if (!lpath) + goto abort_cpio_o; + bytes += printf("%s", lpath); + free(lpath); + } else { /* S_ISREG */ + int fd = xopen(name, O_RDONLY); + fflush_all(); + /* We must abort if file got shorter too! */ + bb_copyfd_exact_size(fd, STDOUT_FILENO, st.st_size); + bytes += st.st_size; + close(fd); + } + bytes = cpio_pad4(bytes); + } + + if (!line) { + if (name != cpio_TRAILER) + goto next_link; + /* TODO: GNU cpio pads trailer to 512 bytes, do we want that? */ + return EXIT_SUCCESS; + } + + free(line); + } /* end of "while (1)" */ +} +#endif + +int cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +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; +#if ENABLE_LONG_OPTS + const char *long_opts = + "extract\0" No_argument "i" + "list\0" No_argument "t" +#if ENABLE_FEATURE_CPIO_O + "create\0" No_argument "o" + "format\0" Required_argument "H" +#if ENABLE_FEATURE_CPIO_P + "pass-through\0" No_argument "p" +#endif +#endif + "owner\0" Required_argument "R" + "verbose\0" No_argument "v" + "null\0" No_argument "0" + "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; + + /* As of now we do not enforce this: */ + /* -i,-t,-o,-p are mutually exclusive */ + /* -u,-d,-m make sense only with -i or -p */ + /* -L makes sense only with -o or -p */ + +#if !ENABLE_FEATURE_CPIO_O + opt = getopt32long(argv, OPTION_STR, long_opts, &cpio_filename, &cpio_owner); +#else + opt = getopt32long(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"), long_opts, + &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 + if ((opt & (OPT_FILE|OPT_CREATE)) == OPT_FILE) { /* -F without -o */ + xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO); + } + if (opt & OPT_PASSTHROUGH) { + pid_t pid; + struct fd_pair pp; + + if (argv[0] == NULL) + bb_show_usage(); + if (opt & OPT_CREATE_LEADING_DIR) + mkdir(argv[0], 0777); + /* Crude existence check: + * close(xopen(argv[0], O_RDONLY | O_DIRECTORY)); + * We can also xopen, fstat, IS_DIR, later fchdir. + * This would check for existence earlier and cleaner. + * As it stands now, if we fail xchdir later, + * child dies on EPIPE, unless it caught + * a diffrerent problem earlier. + * This is good enough for now. + */ +#if !BB_MMU + pp.rd = 3; + pp.wr = 4; + if (!re_execed) { + close(3); + close(4); + xpiped_pair(pp); + } +#else + xpiped_pair(pp); +#endif + pid = fork_or_rexec(argv - optind); + if (pid == 0) { /* child */ + close(pp.rd); + xmove_fd(pp.wr, STDOUT_FILENO); + goto dump; + } + /* parent */ + USE_FOR_NOMMU(argv[-optind][0] &= 0x7f); /* undo fork_or_rexec() damage */ + xchdir(*argv++); + close(pp.wr); + xmove_fd(pp.rd, STDIN_FILENO); + //opt &= ~OPT_PASSTHROUGH; + opt |= OPT_EXTRACT; + goto skip; + } + /* -o */ + if (opt & OPT_CREATE) { + if (cpio_fmt[0] != 'n') /* we _require_ "-H newc" */ + bb_show_usage(); + if (opt & OPT_FILE) { + xmove_fd(xopen(cpio_filename, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); + } + dump: + return cpio_o(); + } + skip: +#endif + + /* One of either extract or test options must be given */ + if ((opt & (OPT_TEST | OPT_EXTRACT)) == 0) { + bb_show_usage(); + } + + if (opt & OPT_TEST) { + /* if both extract and test options are given, ignore extract option */ + opt &= ~OPT_EXTRACT; + archive_handle->action_header = header_list; + } + if (opt & OPT_EXTRACT) { + archive_handle->action_data = data_extract_all; + if (opt & OPT_2STDOUT) + archive_handle->action_data = data_extract_to_stdout; + } + if (opt & OPT_UNCONDITIONAL) { + archive_handle->ah_flags |= ARCHIVE_UNLINK_OLD; + archive_handle->ah_flags &= ~ARCHIVE_EXTRACT_NEWER; + } + if (opt & OPT_VERBOSE) { + if (archive_handle->action_header == header_list) { + archive_handle->action_header = header_verbose_list; + } else { + archive_handle->action_header = header_list; + } + } + if (opt & OPT_CREATE_LEADING_DIR) { + archive_handle->ah_flags |= ARCHIVE_CREATE_LEADING_DIRS; + } + if (opt & OPT_PRESERVE_MTIME) { + archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE; + } + + while (*argv) { + archive_handle->filter = filter_accept_list; + llist_add_to(&archive_handle->accept, *argv); + argv++; + } + + /* see get_header_cpio */ + archive_handle->cpio__blocks = (off_t)-1; + while (get_header_cpio(archive_handle) == EXIT_SUCCESS) + continue; + + create_links_from_list(archive_handle->link_placeholders); + + if (archive_handle->cpio__blocks != (off_t)-1 + && !(opt & OPT_QUIET) + ) { + fprintf(stderr, "%"OFF_FMT"u blocks\n", archive_handle->cpio__blocks); + } + + return EXIT_SUCCESS; +} diff --git a/busybox/archival/dpkg.c b/busybox/archival/dpkg.c new file mode 100644 index 00000000000000..58bc4dba3bc774 --- /dev/null +++ b/busybox/archival/dpkg.c @@ -0,0 +1,1943 @@ +/* vi: set sw=4 ts=4: */ +/* + * mini dpkg implementation for busybox. + * this is not meant as a replacement for dpkg + * + * written by glenn mcgrath with the help of others + * copyright (c) 2001 by glenn mcgrath + * + * parts of the version comparison code is plucked from the real dpkg + * application which is licensed GPLv2 and + * copyright (c) 1995 Ian Jackson + * + * started life as a busybox implementation of udpkg + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* + * known difference between busybox dpkg and the official dpkg that i don't + * consider important, its worth keeping a note of differences anyway, just to + * make it easier to maintain. + * - the first value for the confflile: field isn't placed on a new line. + * - when installing a package the status: field is placed at the end of the + * section, rather than just after the package: field. + * + * bugs that need to be fixed + * - (unknown, please let me know when you find any) + */ +//config:config DPKG +//config: bool "dpkg (44 kb)" +//config: default y +//config: select FEATURE_SEAMLESS_GZ +//config: help +//config: dpkg is a medium-level tool to install, build, remove and manage +//config: Debian packages. +//config: +//config: This implementation of dpkg has a number of limitations, +//config: you should use the official dpkg if possible. + +//applet:IF_DPKG(APPLET(dpkg, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_DPKG) += dpkg.o + +//usage:#define dpkg_trivial_usage +//usage: "[-ilCPru] [-F OPT] PACKAGE" +//usage:#define dpkg_full_usage "\n\n" +//usage: "Install, remove and manage Debian packages\n" +//usage: IF_LONG_OPTS( +//usage: "\n -i,--install Install the package" +//usage: "\n -l,--list List of installed packages" +//usage: "\n --configure Configure an unpackaged package" +//usage: "\n -P,--purge Purge all files of a package" +//usage: "\n -r,--remove Remove all but the configuration files for a package" +//usage: "\n --unpack Unpack a package, but don't configure it" +//usage: "\n --force-depends Ignore dependency problems" +//usage: "\n --force-confnew Overwrite existing config files when installing" +//usage: "\n --force-confold Keep old config files when installing" +//usage: ) +//usage: IF_NOT_LONG_OPTS( +//usage: "\n -i Install the package" +//usage: "\n -l List of installed packages" +//usage: "\n -C Configure an unpackaged package" +//usage: "\n -P Purge all files of a package" +//usage: "\n -r Remove all but the configuration files for a package" +//usage: "\n -u Unpack a package, but don't configure it" +//usage: "\n -F depends Ignore dependency problems" +//usage: "\n -F confnew Overwrite existing config files when installing" +//usage: "\n -F confold Keep old config files when installing" +//usage: ) + +#include "libbb.h" +#include +#include "bb_archive.h" + +/* note: if you vary hash_prime sizes be aware, + * 1) tweaking these will have a big effect on how much memory this program uses. + * 2) for computational efficiency these hash tables should be at least 20% + * larger than the maximum number of elements stored in it. + * 3) all _hash_prime's must be a prime number or chaos is assured, if your looking + * for a prime, try http://www.utm.edu/research/primes/lists/small/10000.txt + * 4) if you go bigger than 15 bits you may get into trouble (untested) as its + * sometimes cast to an unsigned, if you go to 16 bit you will overlap + * int's and chaos is assured, 16381 is the max prime for 14 bit field + */ + +/* NAME_HASH_PRIME, Stores package names and versions, + * I estimate it should be at least 50% bigger than PACKAGE_HASH_PRIME, + * as there a lot of duplicate version numbers */ +#define NAME_HASH_PRIME 16381 + +/* PACKAGE_HASH_PRIME, Maximum number of unique packages, + * It must not be smaller than STATUS_HASH_PRIME, + * Currently only packages from status_hashtable are stored in here, but in + * future this may be used to store packages not only from a status file, + * but an available_hashtable, and even multiple packages files. + * Package can be stored more than once if they have different versions. + * e.g. The same package may have different versions in the status file + * and available file */ +#define PACKAGE_HASH_PRIME 10007 +typedef struct edge_s { + unsigned operator:4; /* was:3 */ + unsigned type:4; + unsigned name:16; /* was:14 */ + unsigned version:16; /* was:14 */ +} edge_t; + +typedef struct common_node_s { + unsigned name:16; /* was:14 */ + unsigned version:16; /* was:14 */ + unsigned num_of_edges:16; /* was:14 */ + edge_t **edge; +} common_node_t; + +/* Currently it doesn't store packages that have state-status of not-installed + * So it only really has to be the size of the maximum number of packages + * likely to be installed at any one time, so there is a bit of leeway here */ +#define STATUS_HASH_PRIME 8191 +typedef struct status_node_s { + unsigned package:16; /* was:14 */ /* has to fit PACKAGE_HASH_PRIME */ + unsigned status:16; /* was:14 */ /* has to fit STATUS_HASH_PRIME */ +} status_node_t; + + +/* Globals */ +struct globals { + char *name_hashtable[NAME_HASH_PRIME + 1]; + common_node_t *package_hashtable[PACKAGE_HASH_PRIME + 1]; + status_node_t *status_hashtable[STATUS_HASH_PRIME + 1]; +}; +#define G (*ptr_to_globals) +#define name_hashtable (G.name_hashtable ) +#define package_hashtable (G.package_hashtable) +#define status_hashtable (G.status_hashtable ) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ +} while (0) + + +/* Even numbers are for 'extras', like ored dependencies or null */ +enum edge_type_e { + EDGE_NULL = 0, + EDGE_PRE_DEPENDS = 1, + EDGE_OR_PRE_DEPENDS = 2, + EDGE_DEPENDS = 3, + EDGE_OR_DEPENDS = 4, + EDGE_REPLACES = 5, + EDGE_PROVIDES = 7, + EDGE_CONFLICTS = 9, + EDGE_SUGGESTS = 11, + EDGE_RECOMMENDS = 13, + EDGE_ENHANCES = 15 +}; +enum operator_e { + VER_NULL = 0, + VER_EQUAL = 1, + VER_LESS = 2, + VER_LESS_EQUAL = 3, + VER_MORE = 4, + VER_MORE_EQUAL = 5, + VER_ANY = 6 +}; + +typedef struct deb_file_s { + char *control_file; + char *filename; + unsigned package:16; /* was:14 */ +} deb_file_t; + + +static void make_hash(const char *key, unsigned *start, unsigned *decrement, const int hash_prime) +{ + unsigned long hash_num = key[0]; + int len = strlen(key); + int i; + + /* Maybe i should have uses a "proper" hashing algorithm here instead + * of making one up myself, seems to be working ok though. */ + for (i = 1; i < len; i++) { + /* shifts the ascii based value and adds it to previous value + * shift amount is mod 24 because long int is 32 bit and data + * to be shifted is 8, don't want to shift data to where it has + * no effect */ + hash_num += (key[i] + key[i-1]) << ((key[i] * i) % 24); + } + *start = (unsigned) hash_num % hash_prime; + *decrement = (unsigned) 1 + (hash_num % (hash_prime - 1)); +} + +/* this adds the key to the hash table */ +static int search_name_hashtable(const char *key) +{ + unsigned probe_address; + unsigned probe_decrement; + + make_hash(key, &probe_address, &probe_decrement, NAME_HASH_PRIME); + while (name_hashtable[probe_address] != NULL) { + if (strcmp(name_hashtable[probe_address], key) == 0) { + return probe_address; + } + probe_address -= probe_decrement; + if ((int)probe_address < 0) { + probe_address += NAME_HASH_PRIME; + } + } + name_hashtable[probe_address] = xstrdup(key); + return probe_address; +} + +/* this DOESN'T add the key to the hashtable + * TODO make it consistent with search_name_hashtable + */ +static unsigned search_status_hashtable(const char *key) +{ + unsigned probe_address; + unsigned probe_decrement; + + make_hash(key, &probe_address, &probe_decrement, STATUS_HASH_PRIME); + while (status_hashtable[probe_address] != NULL) { + if (strcmp(key, name_hashtable[package_hashtable[status_hashtable[probe_address]->package]->name]) == 0) { + break; + } + probe_address -= probe_decrement; + if ((int)probe_address < 0) { + probe_address += STATUS_HASH_PRIME; + } + } + return probe_address; +} + +static int order(char x) +{ + return (x == '~' ? -1 + : x == '\0' ? 0 + : isdigit(x) ? 0 + : isalpha(x) ? x + : (unsigned char)x + 256 + ); +} + +/* This code is taken from dpkg and modified slightly to work with busybox */ +static int version_compare_part(const char *val, const char *ref) +{ + if (!val) val = ""; + if (!ref) ref = ""; + + while (*val || *ref) { + int first_diff; + + while ((*val && !isdigit(*val)) || (*ref && !isdigit(*ref))) { + int vc = order(*val); + int rc = order(*ref); + if (vc != rc) + return vc - rc; + val++; + ref++; + } + + while (*val == '0') + val++; + while (*ref == '0') + ref++; + + first_diff = 0; + while (isdigit(*val) && isdigit(*ref)) { + if (first_diff == 0) + first_diff = *val - *ref; + val++; + ref++; + } + if (isdigit(*val)) + return 1; + if (isdigit(*ref)) + return -1; + if (first_diff) + return first_diff; + } + return 0; +} + +/* if ver1 < ver2 return -1, + * if ver1 = ver2 return 0, + * if ver1 > ver2 return 1, + */ +static int version_compare(const unsigned ver1, const unsigned ver2) +{ + char *ch_ver1 = name_hashtable[ver1]; + char *ch_ver2 = name_hashtable[ver2]; + unsigned epoch1 = 0, epoch2 = 0; + char *colon; + char *deb_ver1, *deb_ver2; + char *upstream_ver1; + char *upstream_ver2; + int result; + + /* Compare epoch */ + colon = strchr(ch_ver1, ':'); + if (colon) { + epoch1 = atoi(ch_ver1); + ch_ver1 = colon + 1; + } + colon = strchr(ch_ver2, ':'); + if (colon) { + epoch2 = atoi(ch_ver2); + ch_ver2 = colon + 1; + } + if (epoch1 < epoch2) { + return -1; + } + if (epoch1 > epoch2) { + return 1; + } + + /* Compare upstream version */ + upstream_ver1 = xstrdup(ch_ver1); + upstream_ver2 = xstrdup(ch_ver2); + + /* Chop off debian version, and store for later use */ + deb_ver1 = strrchr(upstream_ver1, '-'); + deb_ver2 = strrchr(upstream_ver2, '-'); + if (deb_ver1) { + *deb_ver1++ = '\0'; + } + if (deb_ver2) { + *deb_ver2++ = '\0'; + } + result = version_compare_part(upstream_ver1, upstream_ver2); + if (result == 0) { + /* Compare debian versions */ + result = version_compare_part(deb_ver1, deb_ver2); + } + + free(upstream_ver1); + free(upstream_ver2); + return result; +} + +static int test_version(const unsigned version1, const unsigned version2, const unsigned operator) +{ + const int version_result = version_compare(version1, version2); + switch (operator) { + case VER_ANY: + return TRUE; + case VER_EQUAL: + return (version_result == 0); + case VER_LESS: + return (version_result < 0); + case VER_LESS_EQUAL: + return (version_result <= 0); + case VER_MORE: + return (version_result > 0); + case VER_MORE_EQUAL: + return (version_result >= 0); + } + return FALSE; +} + +static int search_package_hashtable(const unsigned name, const unsigned version, const unsigned operator) +{ + unsigned probe_address; + unsigned probe_decrement; + + make_hash(name_hashtable[name], &probe_address, &probe_decrement, PACKAGE_HASH_PRIME); + while (package_hashtable[probe_address] != NULL) { + if (package_hashtable[probe_address]->name == name) { + if (operator == VER_ANY) { + return probe_address; + } + if (test_version(package_hashtable[probe_address]->version, version, operator)) { + return probe_address; + } + } + probe_address -= probe_decrement; + if ((int)probe_address < 0) { + probe_address += PACKAGE_HASH_PRIME; + } + } + return probe_address; +} + +/* + * This function searches through the entire package_hashtable looking + * for a package which provides "needle". It returns the index into + * the package_hashtable for the providing package. + * + * needle is the index into name_hashtable of the package we are + * looking for. + * + * start_at is the index in the package_hashtable to start looking + * at. If start_at is -1 then start at the beginning. This is to allow + * for repeated searches since more than one package might provide + * needle. + * + * FIXME: I don't think this is very efficient, but I thought I'd keep + * it simple for now until it proves to be a problem. + */ +static int search_for_provides(int needle, int start_at) +{ + int i, j; + common_node_t *p; + for (i = start_at + 1; i < PACKAGE_HASH_PRIME; i++) { + p = package_hashtable[i]; + if (p == NULL) + continue; + for (j = 0; j < p->num_of_edges; j++) + if (p->edge[j]->type == EDGE_PROVIDES && p->edge[j]->name == needle) + return i; + } + return -1; +} + +/* + * Add an edge to a node + */ +static void add_edge_to_node(common_node_t *node, edge_t *edge) +{ + node->edge = xrealloc_vector(node->edge, 2, node->num_of_edges); + node->edge[node->num_of_edges++] = edge; +} + +/* + * Create one new node and one new edge for every dependency. + * + * Dependencies which contain multiple alternatives are represented as + * an EDGE_OR_PRE_DEPENDS or EDGE_OR_DEPENDS node, followed by a + * number of EDGE_PRE_DEPENDS or EDGE_DEPENDS nodes. The name field of + * the OR edge contains the full dependency string while the version + * field contains the number of EDGE nodes which follow as part of + * this alternative. + */ +static void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned edge_type) +{ + char *line = xstrdup(whole_line); + char *line2; + char *line_ptr1 = NULL; + char *line_ptr2 = NULL; + char *field; + char *field2; + char *version; + edge_t *edge; + edge_t *or_edge; + int offset_ch; + + field = strtok_r(line, ",", &line_ptr1); + do { + /* skip leading spaces */ + field += strspn(field, " "); + line2 = xstrdup(field); + field2 = strtok_r(line2, "|", &line_ptr2); + or_edge = NULL; + if ((edge_type == EDGE_DEPENDS || edge_type == EDGE_PRE_DEPENDS) + && (strcmp(field, field2) != 0) + ) { + or_edge = xzalloc(sizeof(edge_t)); + or_edge->type = edge_type + 1; + or_edge->name = search_name_hashtable(field); + //or_edge->version = 0; // tracks the number of alternatives + add_edge_to_node(parent_node, or_edge); + } + + do { + edge = xmalloc(sizeof(edge_t)); + edge->type = edge_type; + + /* Skip any extra leading spaces */ + field2 += strspn(field2, " "); + + /* Get dependency version info */ + version = strchr(field2, '('); + if (version == NULL) { + edge->operator = VER_ANY; + /* Get the versions hash number, adding it if the number isn't already in there */ + edge->version = search_name_hashtable("ANY"); + } else { + /* Skip leading ' ' or '(' */ + version += strspn(version, " ("); + /* Calculate length of any operator characters */ + offset_ch = strspn(version, "<=>"); + /* Determine operator */ + if (offset_ch > 0) { + if (strncmp(version, "=", offset_ch) == 0) { + edge->operator = VER_EQUAL; + } else if (strncmp(version, "<<", offset_ch) == 0) { + edge->operator = VER_LESS; + } else if (strncmp(version, "<=", offset_ch) == 0) { + edge->operator = VER_LESS_EQUAL; + } else if (strncmp(version, ">>", offset_ch) == 0) { + edge->operator = VER_MORE; + } else if (strncmp(version, ">=", offset_ch) == 0) { + edge->operator = VER_MORE_EQUAL; + } else { + bb_error_msg_and_die("illegal operator"); + } + } + /* skip to start of version numbers */ + version += offset_ch; + version += strspn(version, " "); + + /* Truncate version at trailing ' ' or ')' */ + version[strcspn(version, " )")] = '\0'; + /* Get the versions hash number, adding it if the number isn't already in there */ + edge->version = search_name_hashtable(version); + } + + /* Get the dependency name */ + field2[strcspn(field2, " (")] = '\0'; + edge->name = search_name_hashtable(field2); + + if (or_edge) + or_edge->version++; + + add_edge_to_node(parent_node, edge); + field2 = strtok_r(NULL, "|", &line_ptr2); + } while (field2 != NULL); + + free(line2); + field = strtok_r(NULL, ",", &line_ptr1); + } while (field != NULL); + + free(line); +} + +static void free_package(common_node_t *node) +{ + unsigned i; + if (node) { + for (i = 0; i < node->num_of_edges; i++) { + free(node->edge[i]); + } + free(node->edge); + free(node); + } +} + +/* + * Gets the next package field from package_buffer, separated into the field name + * and field value, it returns the int offset to the first character of the next field + */ +static int read_package_field(const char *package_buffer, char **field_name, char **field_value) +{ + int offset_name_start = 0; + int offset_name_end = 0; + int offset_value_start = 0; + int offset_value_end = 0; + int offset = 0; + int next_offset; + int name_length; + int value_length; + int exit_flag = FALSE; + + if (package_buffer == NULL) { + *field_name = NULL; + *field_value = NULL; + return -1; + } + while (1) { + next_offset = offset + 1; + switch (package_buffer[offset]) { + case '\0': + exit_flag = TRUE; + break; + case ':': + if (offset_name_end == 0) { + offset_name_end = offset; + offset_value_start = next_offset; + } + /* TODO: Name might still have trailing spaces if ':' isn't + * immediately after name */ + break; + case '\n': + /* TODO: The char next_offset may be out of bounds */ + if (package_buffer[next_offset] != ' ') { + exit_flag = TRUE; + break; + } + case '\t': + case ' ': + /* increment the value start point if its a just filler */ + if (offset_name_start == offset) { + offset_name_start++; + } + if (offset_value_start == offset) { + offset_value_start++; + } + break; + } + if (exit_flag) { + /* Check that the names are valid */ + offset_value_end = offset; + name_length = offset_name_end - offset_name_start; + value_length = offset_value_end - offset_value_start; + if (name_length == 0) { + break; + } + if ((name_length > 0) && (value_length > 0)) { + break; + } + + /* If not valid, start fresh with next field */ + exit_flag = FALSE; + offset_name_start = offset + 1; + offset_name_end = 0; + offset_value_start = offset + 1; + offset_value_end = offset + 1; + offset++; + } + offset++; + } + *field_name = NULL; + if (name_length) { + *field_name = xstrndup(&package_buffer[offset_name_start], name_length); + } + *field_value = NULL; + if (value_length > 0) { + *field_value = xstrndup(&package_buffer[offset_value_start], value_length); + } + return next_offset; +} + +static unsigned fill_package_struct(char *control_buffer) +{ + static const char field_names[] ALIGN1 = + "Package\0""Version\0" + "Pre-Depends\0""Depends\0""Replaces\0""Provides\0" + "Conflicts\0""Suggests\0""Recommends\0""Enhances\0"; + + common_node_t *new_node = xzalloc(sizeof(common_node_t)); + char *field_name; + char *field_value; + int field_start = 0; + int num = -1; + int buffer_length = strlen(control_buffer); + + new_node->version = search_name_hashtable("unknown"); + while (field_start < buffer_length) { + unsigned field_num; + + field_start += read_package_field(&control_buffer[field_start], + &field_name, &field_value); + + if (field_name == NULL) { + goto fill_package_struct_cleanup; + } + + field_num = index_in_strings(field_names, field_name); + switch (field_num) { + case 0: /* Package */ + new_node->name = search_name_hashtable(field_value); + break; + case 1: /* Version */ + new_node->version = search_name_hashtable(field_value); + break; + case 2: /* Pre-Depends */ + add_split_dependencies(new_node, field_value, EDGE_PRE_DEPENDS); + break; + case 3: /* Depends */ + add_split_dependencies(new_node, field_value, EDGE_DEPENDS); + break; + case 4: /* Replaces */ + add_split_dependencies(new_node, field_value, EDGE_REPLACES); + break; + case 5: /* Provides */ + add_split_dependencies(new_node, field_value, EDGE_PROVIDES); + break; + case 6: /* Conflicts */ + add_split_dependencies(new_node, field_value, EDGE_CONFLICTS); + break; + case 7: /* Suggests */ + add_split_dependencies(new_node, field_value, EDGE_SUGGESTS); + break; + case 8: /* Recommends */ + add_split_dependencies(new_node, field_value, EDGE_RECOMMENDS); + break; + case 9: /* Enhances */ + add_split_dependencies(new_node, field_value, EDGE_ENHANCES); + break; + } + fill_package_struct_cleanup: + free(field_name); + free(field_value); + } + + if (new_node->version == search_name_hashtable("unknown")) { + free_package(new_node); + return -1; + } + num = search_package_hashtable(new_node->name, new_node->version, VER_EQUAL); + free_package(package_hashtable[num]); + package_hashtable[num] = new_node; + return num; +} + +/* if num = 1, it returns the want status, 2 returns flag, 3 returns status */ +static unsigned get_status(const unsigned status_node, const int num) +{ + char *status_string = name_hashtable[status_hashtable[status_node]->status]; + char *state_sub_string; + unsigned state_sub_num; + int len; + int i; + + /* set tmp_string to point to the start of the word number */ + for (i = 1; i < num; i++) { + /* skip past a word */ + status_string += strcspn(status_string, " "); + /* skip past the separating spaces */ + status_string += strspn(status_string, " "); + } + len = strcspn(status_string, " \n"); + state_sub_string = xstrndup(status_string, len); + state_sub_num = search_name_hashtable(state_sub_string); + free(state_sub_string); + return state_sub_num; +} + +static void set_status(const unsigned status_node_num, const char *new_value, const int position) +{ + const unsigned new_value_num = search_name_hashtable(new_value); + unsigned want = get_status(status_node_num, 1); + unsigned flag = get_status(status_node_num, 2); + unsigned status = get_status(status_node_num, 3); + char *new_status; + + switch (position) { + case 1: + want = new_value_num; + break; + case 2: + flag = new_value_num; + break; + case 3: + status = new_value_num; + break; + default: + bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen"); + } + + new_status = xasprintf("%s %s %s", name_hashtable[want], name_hashtable[flag], name_hashtable[status]); + status_hashtable[status_node_num]->status = search_name_hashtable(new_status); + free(new_status); +} + +static const char *describe_status(int status_num) +{ + int status_want, status_state; + if (status_hashtable[status_num] == NULL || status_hashtable[status_num]->status == 0) + return "is not installed or flagged to be installed"; + + status_want = get_status(status_num, 1); + status_state = get_status(status_num, 3); + + if (status_state == search_name_hashtable("installed")) { + if (status_want == search_name_hashtable("install")) + return "is installed"; + if (status_want == search_name_hashtable("deinstall")) + return "is marked to be removed"; + if (status_want == search_name_hashtable("purge")) + return "is marked to be purged"; + } + if (status_want == search_name_hashtable("unknown")) + return "is in an indeterminate state"; + if (status_want == search_name_hashtable("install")) + return "is marked to be installed"; + + return "is not installed or flagged to be installed"; +} + +static void index_status_file(const char *filename) +{ + FILE *status_file; + char *control_buffer; + char *status_line; + status_node_t *status_node = NULL; + unsigned status_num; + + status_file = xfopen_for_read(filename); + while ((control_buffer = xmalloc_fgetline_str(status_file, "\n\n")) != NULL) { + const unsigned package_num = fill_package_struct(control_buffer); + if (package_num != -1) { + status_node = xmalloc(sizeof(status_node_t)); + /* fill_package_struct doesn't handle the status field */ + status_line = strstr(control_buffer, "Status:"); + if (status_line != NULL) { + status_line += 7; + status_line += strspn(status_line, " \n\t"); + status_line = xstrndup(status_line, strcspn(status_line, "\n")); + status_node->status = search_name_hashtable(status_line); + free(status_line); + } + status_node->package = package_num; + status_num = search_status_hashtable(name_hashtable[package_hashtable[status_node->package]->name]); + status_hashtable[status_num] = status_node; + } + free(control_buffer); + } + fclose(status_file); +} + +static void write_buffer_no_status(FILE *new_status_file, const char *control_buffer) +{ + char *name; + char *value; + int start = 0; + while (1) { + start += read_package_field(&control_buffer[start], &name, &value); + if (name == NULL) { + break; + } + if (strcmp(name, "Status") != 0) { + fprintf(new_status_file, "%s: %s\n", name, value); + } + } +} + +/* This could do with a cleanup */ +static void write_status_file(deb_file_t **deb_file) +{ + FILE *old_status_file = xfopen_for_read("/var/lib/dpkg/status"); + FILE *new_status_file = xfopen_for_write("/var/lib/dpkg/status.udeb"); + char *package_name; + char *status_from_file; + char *control_buffer = NULL; + char *tmp_string; + int status_num; + int field_start = 0; + int write_flag; + int i = 0; + + /* Update previously known packages */ + while ((control_buffer = xmalloc_fgetline_str(old_status_file, "\n\n")) != NULL) { + tmp_string = strstr(control_buffer, "Package:"); + if (tmp_string == NULL) { + continue; + } + + tmp_string += 8; + tmp_string += strspn(tmp_string, " \n\t"); + package_name = xstrndup(tmp_string, strcspn(tmp_string, "\n")); + write_flag = FALSE; + tmp_string = strstr(control_buffer, "Status:"); + if (tmp_string != NULL) { + /* Separate the status value from the control buffer */ + tmp_string += 7; + tmp_string += strspn(tmp_string, " \n\t"); + status_from_file = xstrndup(tmp_string, strcspn(tmp_string, "\n")); + } else { + status_from_file = NULL; + } + + /* Find this package in the status hashtable */ + status_num = search_status_hashtable(package_name); + if (status_hashtable[status_num] != NULL) { + const char *status_from_hashtable = name_hashtable[status_hashtable[status_num]->status]; + if (strcmp(status_from_file, status_from_hashtable) != 0) { + /* New status isn't exactly the same as old status */ + const int state_status = get_status(status_num, 3); + if ((strcmp("installed", name_hashtable[state_status]) == 0) + || (strcmp("unpacked", name_hashtable[state_status]) == 0) + ) { + /* We need to add the control file from the package */ + i = 0; + while (deb_file[i] != NULL) { + if (strcmp(package_name, name_hashtable[package_hashtable[deb_file[i]->package]->name]) == 0) { + /* Write a status file entry with a modified status */ + /* remove trailing \n's */ + write_buffer_no_status(new_status_file, deb_file[i]->control_file); + set_status(status_num, "ok", 2); + fprintf(new_status_file, "Status: %s\n\n", + name_hashtable[status_hashtable[status_num]->status]); + write_flag = TRUE; + break; + } + i++; + } + /* This is temperary, debugging only */ + if (deb_file[i] == NULL) { + bb_error_msg_and_die("ALERT: cannot find a control file, " + "your status file may be broken, status may be " + "incorrect for %s", package_name); + } + } + else if (strcmp("not-installed", name_hashtable[state_status]) == 0) { + /* Only write the Package, Status, Priority and Section lines */ + fprintf(new_status_file, "Package: %s\n", package_name); + fprintf(new_status_file, "Status: %s\n", status_from_hashtable); + + while (1) { + char *field_name; + char *field_value; + field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value); + if (field_name == NULL) { + break; + } + if ((strcmp(field_name, "Priority") == 0) + || (strcmp(field_name, "Section") == 0) + ) { + fprintf(new_status_file, "%s: %s\n", field_name, field_value); + } + } + write_flag = TRUE; + fputs("\n", new_status_file); + } + else if (strcmp("config-files", name_hashtable[state_status]) == 0) { + /* only change the status line */ + while (1) { + char *field_name; + char *field_value; + field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value); + if (field_name == NULL) { + break; + } + /* Setup start point for next field */ + if (strcmp(field_name, "Status") == 0) { + fprintf(new_status_file, "Status: %s\n", status_from_hashtable); + } else { + fprintf(new_status_file, "%s: %s\n", field_name, field_value); + } + } + write_flag = TRUE; + fputs("\n", new_status_file); + } + } + } + /* If the package from the status file wasn't handle above, do it now*/ + if (!write_flag) { + fprintf(new_status_file, "%s\n\n", control_buffer); + } + + free(status_from_file); + free(package_name); + free(control_buffer); + } + + /* Write any new packages */ + for (i = 0; deb_file[i] != NULL; i++) { + status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[i]->package]->name]); + if (strcmp("reinstreq", name_hashtable[get_status(status_num, 2)]) == 0) { + write_buffer_no_status(new_status_file, deb_file[i]->control_file); + set_status(status_num, "ok", 2); + fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]); + } + } + fclose(old_status_file); + fclose(new_status_file); + + /* Create a separate backfile to dpkg */ + if (rename("/var/lib/dpkg/status", "/var/lib/dpkg/status.udeb.bak") == -1) { + if (errno != ENOENT) + bb_error_msg_and_die("can't create backup status file"); + /* Its ok if renaming the status file fails because status + * file doesn't exist, maybe we are starting from scratch */ + bb_error_msg("no status file found, creating new one"); + } + + xrename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status"); +} + +/* This function returns TRUE if the given package can satisfy a + * dependency of type depend_type. + * + * A pre-depends is satisfied only if a package is already installed, + * which a regular depends can be satisfied by a package which we want + * to install. + */ +static int package_satisfies_dependency(int package, int depend_type) +{ + int status_num = search_status_hashtable(name_hashtable[package_hashtable[package]->name]); + + /* status could be unknown if package is a pure virtual + * provides which cannot satisfy any dependency by itself. + */ + if (status_hashtable[status_num] == NULL) + return 0; + + switch (depend_type) { + case EDGE_PRE_DEPENDS: return get_status(status_num, 3) == search_name_hashtable("installed"); + case EDGE_DEPENDS: return get_status(status_num, 1) == search_name_hashtable("install"); + } + return 0; +} + +static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count - ?? */) +{ + int *conflicts = NULL; + int conflicts_num = 0; + int i = deb_start; + int j; + + /* Check for conflicts + * TODO: TEST if conflicts with other packages to be installed + * + * Add install packages and the packages they provide + * to the list of files to check conflicts for + */ + + /* Create array of package numbers to check against + * installed package for conflicts*/ + while (deb_file[i] != NULL) { + const unsigned package_num = deb_file[i]->package; + conflicts = xrealloc_vector(conflicts, 2, conflicts_num); + conflicts[conflicts_num] = package_num; + conflicts_num++; + /* add provides to conflicts list */ + for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) { + if (package_hashtable[package_num]->edge[j]->type == EDGE_PROVIDES) { + const int conflicts_package_num = search_package_hashtable( + package_hashtable[package_num]->edge[j]->name, + package_hashtable[package_num]->edge[j]->version, + package_hashtable[package_num]->edge[j]->operator); + if (package_hashtable[conflicts_package_num] == NULL) { + /* create a new package */ + common_node_t *new_node = xzalloc(sizeof(common_node_t)); + new_node->name = package_hashtable[package_num]->edge[j]->name; + new_node->version = package_hashtable[package_num]->edge[j]->version; + package_hashtable[conflicts_package_num] = new_node; + } + conflicts = xrealloc_vector(conflicts, 2, conflicts_num); + conflicts[conflicts_num] = conflicts_package_num; + conflicts_num++; + } + } + i++; + } + + /* Check conflicts */ + i = 0; + while (deb_file[i] != NULL) { + const common_node_t *package_node = package_hashtable[deb_file[i]->package]; + int status_num = 0; + status_num = search_status_hashtable(name_hashtable[package_node->name]); + + if (get_status(status_num, 3) == search_name_hashtable("installed")) { + i++; + continue; + } + + for (j = 0; j < package_node->num_of_edges; j++) { + const edge_t *package_edge = package_node->edge[j]; + + if (package_edge->type == EDGE_CONFLICTS) { + const unsigned package_num = + search_package_hashtable(package_edge->name, + package_edge->version, + package_edge->operator); + int result = 0; + if (package_hashtable[package_num] != NULL) { + status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]); + + if (get_status(status_num, 1) == search_name_hashtable("install")) { + result = test_version(package_hashtable[deb_file[i]->package]->version, + package_edge->version, package_edge->operator); + } + } + + if (result) { + bb_error_msg_and_die("package %s conflicts with %s", + name_hashtable[package_node->name], + name_hashtable[package_edge->name]); + } + } + } + i++; + } + + + /* Check dependentcies */ + for (i = 0; i < PACKAGE_HASH_PRIME; i++) { + int status_num = 0; + int number_of_alternatives = 0; + const edge_t * root_of_alternatives = NULL; + const common_node_t *package_node = package_hashtable[i]; + + /* If the package node does not exist then this + * package is a virtual one. In which case there are + * no dependencies to check. + */ + if (package_node == NULL) continue; + + status_num = search_status_hashtable(name_hashtable[package_node->name]); + + /* If there is no status then this package is a + * virtual one provided by something else. In which + * case there are no dependencies to check. + */ + if (status_hashtable[status_num] == NULL) continue; + + /* If we don't want this package installed then we may + * as well ignore it's dependencies. + */ + if (get_status(status_num, 1) != search_name_hashtable("install")) { + continue; + } + + /* This code is tested only for EDGE_DEPENDS, since I + * have no suitable pre-depends available. There is no + * reason that it shouldn't work though :-) + */ + for (j = 0; j < package_node->num_of_edges; j++) { + const edge_t *package_edge = package_node->edge[j]; + unsigned package_num; + + if (package_edge->type == EDGE_OR_PRE_DEPENDS + || package_edge->type == EDGE_OR_DEPENDS + ) { + /* start an EDGE_OR_ list */ + number_of_alternatives = package_edge->version; + root_of_alternatives = package_edge; + continue; + } + if (number_of_alternatives == 0) { /* not in the middle of an EDGE_OR_ list */ + number_of_alternatives = 1; + root_of_alternatives = NULL; + } + + package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator); + + if (package_edge->type == EDGE_PRE_DEPENDS + || package_edge->type == EDGE_DEPENDS + ) { + int result=1; + status_num = 0; + + /* If we are inside an alternative then check + * this edge is the right type. + * + * EDGE_DEPENDS == OR_DEPENDS -1 + * EDGE_PRE_DEPENDS == OR_PRE_DEPENDS -1 + */ + if (root_of_alternatives && package_edge->type != root_of_alternatives->type - 1) + bb_error_msg_and_die("fatal error, package dependencies corrupt: %d != %d - 1", + package_edge->type, root_of_alternatives->type); + + if (package_hashtable[package_num] != NULL) + result = !package_satisfies_dependency(package_num, package_edge->type); + + if (result) { /* check for other package which provide what we are looking for */ + int provider = -1; + + while ((provider = search_for_provides(package_edge->name, provider)) > -1) { + if (package_hashtable[provider] == NULL) { + puts("Have a provider but no package information for it"); + continue; + } + result = !package_satisfies_dependency(provider, package_edge->type); + + if (result == 0) + break; + } + } + + /* It must be already installed, or to be installed */ + number_of_alternatives--; + if (result && number_of_alternatives == 0) { + if (root_of_alternatives) + bb_error_msg_and_die( + "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], + "cannot be satisfied"); + bb_error_msg_and_die( + "package %s %sdepends on %s, which %s", + name_hashtable[package_node->name], + package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "", + name_hashtable[package_edge->name], + describe_status(status_num)); + } + if (result == 0 && number_of_alternatives) { + /* we've found a package which + * satisfies the dependency, + * so skip over the rest of + * the alternatives. + */ + j += number_of_alternatives; + number_of_alternatives = 0; + } + } + } + } + free(conflicts); + return TRUE; +} + +static char **create_list(const char *filename) +{ + FILE *list_stream; + char **file_list; + char *line; + int count; + + /* don't use [xw]fopen here, handle error ourself */ + list_stream = fopen_for_read(filename); + if (list_stream == NULL) { + return NULL; + } + + file_list = NULL; + count = 0; + while ((line = xmalloc_fgetline(list_stream)) != NULL) { + file_list = xrealloc_vector(file_list, 2, count); + file_list[count++] = line; + /*file_list[count] = NULL; - xrealloc_vector did it */ + } + fclose(list_stream); + + return file_list; +} + +/* maybe i should try and hook this into remove_file.c somehow */ +static int remove_file_array(char **remove_names, char **exclude_names) +{ + struct stat path_stat; + int remove_flag = 1; /* not removed anything yet */ + int i, j; + + if (remove_names == NULL) { + return 0; + } + for (i = 0; remove_names[i] != NULL; i++) { + if (exclude_names != NULL) { + for (j = 0; exclude_names[j] != NULL; j++) { + if (strcmp(remove_names[i], exclude_names[j]) == 0) { + goto skip; + } + } + } + /* TODO: why we are checking lstat? we can just try rm/rmdir */ + if (lstat(remove_names[i], &path_stat) < 0) { + continue; + } + if (S_ISDIR(path_stat.st_mode)) { + remove_flag &= rmdir(remove_names[i]); /* 0 if no error */ + } else { + remove_flag &= unlink(remove_names[i]); /* 0 if no error */ + } + skip: + continue; + } + return (remove_flag == 0); +} + +static void run_package_script_or_die(const char *package_name, const char *script_type) +{ + char *script_path; + int result; + + script_path = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, script_type); + + /* If the file doesn't exist it isn't fatal */ + result = access(script_path, F_OK) ? EXIT_SUCCESS : system(script_path); + free(script_path); + if (result) + bb_error_msg_and_die("%s failed, exit code %d", script_type, result); +} + +/* +The policy manual defines what scripts get called when and with +what arguments. I realize that busybox does not support all of +these scenarios, but it does support some of them; it does not, +however, run them with any parameters in run_package_script_or_die(). +Here are the scripts: + +preinst install +preinst install +preinst upgrade +preinst abort_upgrade +postinst configure +postinst abort-upgade +postinst abort-remove +postinst abort-remove in-favour +postinst abort-deconfigure in-favor removing +prerm remove +prerm upgrade +prerm failed-upgrade +prerm remove in-favor +prerm deconfigure in-favour removing +postrm remove +postrm purge +postrm upgrade +postrm failed-upgrade +postrm abort-install +postrm abort-install +postrm abort-upgrade +postrm disappear +*/ +static const char *const all_control_files[] = { + "preinst", "postinst", "prerm", "postrm", + "list", "md5sums", "shlibs", "conffiles", + "config", "templates" +}; + +static char **all_control_list(const char *package_name) +{ + unsigned i = 0; + char **remove_files; + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = xzalloc(sizeof(all_control_files) + sizeof(char*)); + while (i < ARRAY_SIZE(all_control_files)) { + remove_files[i] = xasprintf("/var/lib/dpkg/info/%s.%s", + package_name, all_control_files[i]); + i++; + } + + return remove_files; +} + +static void free_array(char **array) +{ + if (array) { + unsigned i = 0; + while (array[i]) { + free(array[i]); + i++; + } + free(array); + } +} + +/* This function lists information on the installed packages. It loops through + * the status_hashtable to retrieve the info. This results in smaller code than + * scanning the status file. The resulting list, however, is unsorted. + */ +static void list_packages(const char *pattern) +{ + int i; + + puts(" Name Version"); + puts("+++-==============-=============="); + + /* go through status hash, dereference package hash and finally strings */ + for (i = 0; i < STATUS_HASH_PRIME+1; i++) { + if (status_hashtable[i]) { + const char *stat_str; /* status string */ + const char *name_str; /* package name */ + const char *vers_str; /* version */ + char s1, s2; /* status abbreviations */ + int spccnt; /* space count */ + int j; + + stat_str = name_hashtable[status_hashtable[i]->status]; + name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name]; + vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version]; + + if (pattern && fnmatch(pattern, name_str, 0) != 0) + continue; + + /* get abbreviation for status field 1 */ + s1 = stat_str[0] == 'i' ? 'i' : 'r'; + + /* get abbreviation for status field 2 */ + for (j = 0, spccnt = 0; stat_str[j] && spccnt < 2; j++) { + if (stat_str[j] == ' ') spccnt++; + } + s2 = stat_str[j]; + + /* print out the line formatted like Debian dpkg */ + printf("%c%c %-14s %s\n", s1, s2, name_str, vers_str); + } + } +} + +static void remove_package(const unsigned package_num, int noisy) +{ + const char *package_name = name_hashtable[package_hashtable[package_num]->name]; + const char *package_version = name_hashtable[package_hashtable[package_num]->version]; + const unsigned status_num = search_status_hashtable(package_name); + const int package_name_length = strlen(package_name); + char **remove_files; + char **exclude_files; + char list_name[package_name_length + 25]; + char conffile_name[package_name_length + 30]; + + if (noisy) + printf("Removing %s (%s)...\n", package_name, package_version); + + /* Run prerm script */ + run_package_script_or_die(package_name, "prerm"); + + /* Create a list of files to remove, and a separate list of those to keep */ + sprintf(list_name, "/var/lib/dpkg/info/%s.%s", package_name, "list"); + remove_files = create_list(list_name); + + sprintf(conffile_name, "/var/lib/dpkg/info/%s.%s", package_name, "conffiles"); + exclude_files = create_list(conffile_name); + + /* Some directories can't be removed straight away, so do multiple passes */ + while (remove_file_array(remove_files, exclude_files)) + continue; + free_array(exclude_files); + free_array(remove_files); + + /* Create a list of files in /var/lib/dpkg/info/.* to keep */ + exclude_files = xzalloc(sizeof(exclude_files[0]) * 3); + exclude_files[0] = xstrdup(conffile_name); + exclude_files[1] = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "postrm"); + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = all_control_list(package_name); + + remove_file_array(remove_files, exclude_files); + free_array(remove_files); + free_array(exclude_files); + + /* rename .conffiles to .list + * The conffiles control file isn't required in Debian packages, so don't + * error out if it's missing. */ + rename(conffile_name, list_name); + + /* Change package status */ + set_status(status_num, "config-files", 3); +} + +static void purge_package(const unsigned package_num) +{ + const char *package_name = name_hashtable[package_hashtable[package_num]->name]; + const char *package_version = name_hashtable[package_hashtable[package_num]->version]; + const unsigned status_num = search_status_hashtable(package_name); + char **remove_files; + char **exclude_files; + char list_name[strlen(package_name) + 25]; + + printf("Purging %s (%s)...\n", package_name, package_version); + + /* Run prerm script */ + run_package_script_or_die(package_name, "prerm"); + + /* Create a list of files to remove */ + sprintf(list_name, "/var/lib/dpkg/info/%s.%s", package_name, "list"); + remove_files = create_list(list_name); + + /* Some directories cant be removed straight away, so do multiple passes */ + while (remove_file_array(remove_files, NULL)) + continue; + free_array(remove_files); + + /* Create a list of all /var/lib/dpkg/info/ files */ + remove_files = all_control_list(package_name); + + /* Delete all of them except the postrm script */ + exclude_files = xzalloc(sizeof(exclude_files[0]) * 2); + exclude_files[0] = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "postrm"); + remove_file_array(remove_files, exclude_files); + free_array(exclude_files); + + /* Run and remove postrm script */ + run_package_script_or_die(package_name, "postrm"); + remove_file_array(remove_files, NULL); + + free_array(remove_files); + + /* Change package status */ + set_status(status_num, "not-installed", 3); +} + +static archive_handle_t *init_archive_deb_ar(const char *filename) +{ + archive_handle_t *ar_handle; + + /* Setup an ar archive handle that refers to the gzip sub archive */ + ar_handle = init_handle(); + ar_handle->filter = filter_accept_list_reassign; + ar_handle->src_fd = xopen(filename, O_RDONLY); + + return ar_handle; +} + +static void init_archive_deb_control(archive_handle_t *ar_handle) +{ + archive_handle_t *tar_handle; + + /* Setup the tar archive handle */ + tar_handle = init_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; +} + +static void init_archive_deb_data(archive_handle_t *ar_handle) +{ + archive_handle_t *tar_handle; + + /* Setup the tar archive handle */ + tar_handle = init_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; +} + +static void FAST_FUNC data_extract_to_buffer(archive_handle_t *archive_handle) +{ + unsigned size = archive_handle->file_header->size; + + archive_handle->dpkg__buffer = xzalloc(size + 1); + xread(archive_handle->src_fd, archive_handle->dpkg__buffer, size); +} + +static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, llist_t *myaccept) +{ + ar_handle->dpkg__sub_archive->action_data = data_extract_to_buffer; + ar_handle->dpkg__sub_archive->accept = myaccept; + ar_handle->dpkg__sub_archive->filter = filter_accept_list; + + unpack_ar_archive(ar_handle); + close(ar_handle->src_fd); + + return ar_handle->dpkg__sub_archive->dpkg__buffer; +} + +static void append_control_file_to_llist(const char *package_name, const char *control_name, llist_t **ll) +{ + FILE *fp; + char *filename, *line; + + filename = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, control_name); + fp = fopen_for_read(filename); + free(filename); + if (fp != NULL) { + while ((line = xmalloc_fgetline(fp)) != NULL) + llist_add_to(ll, line); + fclose(fp); + } +} + +static char FAST_FUNC filter_rename_config(archive_handle_t *archive_handle) +{ + int fd; + char *name_ptr = archive_handle->file_header->name + 1; + + /* Is this file marked as config file? */ + if (!find_list_entry(archive_handle->accept, name_ptr)) + return EXIT_SUCCESS; /* no */ + + fd = open(name_ptr, O_RDONLY); + if (fd >= 0) { + md5_ctx_t md5; + char *md5line, *buf; + int count; + + /* Calculate MD5 of existing file */ + buf = xmalloc(4096); + md5_begin(&md5); + while ((count = safe_read(fd, buf, 4096)) > 0) + md5_hash(&md5, buf, count); + md5_end(&md5, buf); /* using buf as result storage */ + close(fd); + + md5line = xmalloc(16 * 2 + 2 + strlen(name_ptr) + 1); + sprintf(bin2hex(md5line, buf, 16), " %s", name_ptr); + free(buf); + + /* Is it changed after install? */ + if (find_list_entry(archive_handle->accept, md5line) == NULL) { + printf("Warning: Creating %s as %s.dpkg-new\n", name_ptr, name_ptr); + archive_handle->file_header->name = xasprintf("%s.dpkg-new", archive_handle->file_header->name); + } + free(md5line); + } + return EXIT_SUCCESS; +} + +static void FAST_FUNC data_extract_all_prefix(archive_handle_t *archive_handle) +{ + char *name_ptr = archive_handle->file_header->name; + + /* Skip all leading "/" */ + while (*name_ptr == '/') + name_ptr++; + /* Skip all leading "./" and "../" */ + while (name_ptr[0] == '.') { + if (name_ptr[1] == '.') + name_ptr++; + if (name_ptr[1] != '/') + break; + name_ptr += 2; + } + + if (name_ptr[0] != '\0') { + archive_handle->file_header->name = xasprintf("%s%s", archive_handle->dpkg__buffer, name_ptr); + data_extract_all(archive_handle); + if (fnmatch("*.dpkg-new", archive_handle->file_header->name, 0) == 0) { + /* remove .dpkg-new suffix */ + archive_handle->file_header->name[strlen(archive_handle->file_header->name) - 9] = '\0'; + } + } +} + +enum { + /* Commands */ + OPT_configure = (1 << 0), + OPT_install = (1 << 1), + OPT_list_installed = (1 << 2), + OPT_purge = (1 << 3), + OPT_remove = (1 << 4), + OPT_unpack = (1 << 5), + OPTMASK_cmd = (1 << 6) - 1, + /* Options */ + OPT_force = (1 << 6), + OPT_force_ignore_depends = (1 << 7), + OPT_force_confnew = (1 << 8), + OPT_force_confold = (1 << 9), +}; + +static void unpack_package(deb_file_t *deb_file) +{ + const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name]; + const unsigned status_num = search_status_hashtable(package_name); + const unsigned status_package_num = status_hashtable[status_num]->package; + char *info_prefix; + char *list_filename; + archive_handle_t *archive_handle; + FILE *out_stream; + llist_t *accept_list; + llist_t *conffile_list; + int i; + + /* If existing version, remove it first */ + conffile_list = NULL; + if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) { + /* Package is already installed, remove old version first */ + printf("Preparing to replace %s %s (using %s)...\n", package_name, + name_hashtable[package_hashtable[status_package_num]->version], + deb_file->filename); + + /* Read md5sums from old package */ + if (!(option_mask32 & OPT_force_confold)) + append_control_file_to_llist(package_name, "md5sums", &conffile_list); + + remove_package(status_package_num, 0); + } else { + printf("Unpacking %s (from %s)...\n", package_name, deb_file->filename); + } + + /* Extract control.tar.gz to /var/lib/dpkg/info/.filename */ + info_prefix = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, ""); + archive_handle = init_archive_deb_ar(deb_file->filename); + init_archive_deb_control(archive_handle); + + accept_list = NULL; + i = 0; + while (i < ARRAY_SIZE(all_control_files)) { + char *c = xasprintf("./%s", all_control_files[i]); + llist_add_to(&accept_list, c); + i++; + } + archive_handle->dpkg__sub_archive->accept = accept_list; + archive_handle->dpkg__sub_archive->filter = filter_accept_list; + archive_handle->dpkg__sub_archive->action_data = data_extract_all_prefix; + archive_handle->dpkg__sub_archive->dpkg__buffer = info_prefix; + archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_UNLINK_OLD; + unpack_ar_archive(archive_handle); + + /* Run the preinst prior to extracting */ + run_package_script_or_die(package_name, "preinst"); + + /* Don't overwrite existing config files */ + if (!(option_mask32 & OPT_force_confnew)) + append_control_file_to_llist(package_name, "conffiles", &conffile_list); + + /* Extract data.tar.gz to the root directory */ + archive_handle = init_archive_deb_ar(deb_file->filename); + init_archive_deb_data(archive_handle); + archive_handle->dpkg__sub_archive->accept = conffile_list; + /* Why ARCHIVE_REMEMBER_NAMES? + * We want names collected in ->passed list even if conffile_list + * is NULL (otherwise get_header_tar may optimize name saving out): + */ + archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_REMEMBER_NAMES | ARCHIVE_UNLINK_OLD; + archive_handle->dpkg__sub_archive->filter = filter_rename_config; + archive_handle->dpkg__sub_archive->action_data = data_extract_all_prefix; + archive_handle->dpkg__sub_archive->dpkg__buffer = (char*)"/"; /* huh? */ + unpack_ar_archive(archive_handle); + + /* Create the list file */ + list_filename = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "list"); + out_stream = xfopen_for_write(list_filename); + archive_handle->dpkg__sub_archive->passed = llist_rev(archive_handle->dpkg__sub_archive->passed); + while (archive_handle->dpkg__sub_archive->passed) { + char *filename = llist_pop(&archive_handle->dpkg__sub_archive->passed); + /* the leading . has been stripped by data_extract_all_prefix already */ + fprintf(out_stream, "%s\n", filename); + free(filename); + } + fclose(out_stream); + + /* change status */ + set_status(status_num, "install", 1); + set_status(status_num, "unpacked", 3); + + free(info_prefix); + free(list_filename); +} + +static void configure_package(deb_file_t *deb_file) +{ + const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name]; + const char *package_version = name_hashtable[package_hashtable[deb_file->package]->version]; + const int status_num = search_status_hashtable(package_name); + + printf("Setting up %s (%s)...\n", package_name, package_version); + + /* Run the postinst script */ + /* TODO: handle failure gracefully */ + run_package_script_or_die(package_name, "postinst"); + + /* Change status to reflect success */ + set_status(status_num, "install", 1); + set_status(status_num, "installed", 3); +} + +int dpkg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dpkg_main(int argc UNUSED_PARAM, char **argv) +{ + deb_file_t **deb_file = NULL; + status_node_t *status_node; + char *str_f; + int opt; + int package_num; + int deb_count = 0; + int state_status; + int status_num; + int i; +#if ENABLE_LONG_OPTS + static const char dpkg_longopts[] ALIGN1 = +// FIXME: we use -C non-compatibly, should be: +// "-C|--audit Check for broken package(s)" + "configure\0" No_argument "C" + "force\0" Required_argument "F" + "install\0" No_argument "i" + "list\0" No_argument "l" + "purge\0" No_argument "P" + "remove\0" No_argument "r" + "unpack\0" No_argument "u" + "force-depends\0" No_argument "\xff" + "force-confnew\0" No_argument "\xfe" + "force-confold\0" No_argument "\xfd" + ; +#endif + + INIT_G(); + + opt = getopt32long(argv, "CilPruF:", dpkg_longopts, &str_f); + argv += optind; + //if (opt & OPT_configure) ... // -C + if (opt & OPT_force) { // -F (--force in official dpkg) + if (strcmp(str_f, "depends") == 0) + opt |= OPT_force_ignore_depends; + else if (strcmp(str_f, "confnew") == 0) + opt |= OPT_force_confnew; + else if (strcmp(str_f, "confold") == 0) + opt |= OPT_force_confold; + else + bb_show_usage(); + option_mask32 = opt; + } + //if (opt & OPT_install) ... // -i + //if (opt & OPT_list_installed) ... // -l + //if (opt & OPT_purge) ... // -P + //if (opt & OPT_remove) ... // -r + //if (opt & OPT_unpack) ... // -u (--unpack in official dpkg) + if (!(opt & OPTMASK_cmd) /* no cmd */ + || ((opt & OPTMASK_cmd) & ((opt & OPTMASK_cmd)-1)) /* more than one cmd */ + ) { + bb_show_usage(); + } + +/* puts("(Reading database ... xxxxx files and directories installed.)"); */ + index_status_file("/var/lib/dpkg/status"); + + /* if the list action was given print the installed packages and exit */ + if (opt & OPT_list_installed) { + list_packages(argv[0]); /* param can be NULL */ + return EXIT_SUCCESS; + } + + /* Read arguments and store relevant info in structs */ + while (*argv) { + /* deb_count = nb_elem - 1 and we need nb_elem + 1 to allocate terminal node [NULL pointer] */ + deb_file = xrealloc_vector(deb_file, 2, deb_count); + deb_file[deb_count] = xzalloc(sizeof(deb_file[0][0])); + if (opt & (OPT_install | OPT_unpack)) { + /* -i/-u: require filename */ + archive_handle_t *archive_handle; + llist_t *control_list = NULL; + + /* Extract the control file */ + llist_add_to(&control_list, (char*)"./control"); + archive_handle = init_archive_deb_ar(argv[0]); + init_archive_deb_control(archive_handle); + deb_file[deb_count]->control_file = deb_extract_control_file_to_buffer(archive_handle, control_list); + if (deb_file[deb_count]->control_file == NULL) { + bb_error_msg_and_die("can't extract control file"); + } + deb_file[deb_count]->filename = xstrdup(argv[0]); + package_num = fill_package_struct(deb_file[deb_count]->control_file); + + if (package_num == -1) { + bb_error_msg("invalid control file in %s", argv[0]); + argv++; + continue; + } + deb_file[deb_count]->package = (unsigned) package_num; + + /* Add the package to the status hashtable */ + if (opt & (OPT_unpack | OPT_install)) { + /* Try and find a currently installed version of this package */ + status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]); + /* If no previous entry was found initialise a new entry */ + if (status_hashtable[status_num] == NULL + || status_hashtable[status_num]->status == 0 + ) { + status_node = xmalloc(sizeof(status_node_t)); + status_node->package = deb_file[deb_count]->package; + /* reinstreq isn't changed to "ok" until the package control info + * is written to the status file*/ + status_node->status = search_name_hashtable("install reinstreq not-installed"); + status_hashtable[status_num] = status_node; + } else { + set_status(status_num, "install", 1); + set_status(status_num, "reinstreq", 2); + } + } + } else if (opt & (OPT_configure | OPT_purge | OPT_remove)) { + /* -C/-p/-r: require package name */ + deb_file[deb_count]->package = search_package_hashtable( + search_name_hashtable(argv[0]), + search_name_hashtable("ANY"), VER_ANY); + if (package_hashtable[deb_file[deb_count]->package] == NULL) { + bb_error_msg_and_die("package %s is uninstalled or unknown", argv[0]); + } + package_num = deb_file[deb_count]->package; + status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]); + state_status = get_status(status_num, 3); + + /* check package status is "installed" */ + if (opt & OPT_remove) { + if (strcmp(name_hashtable[state_status], "not-installed") == 0 + || strcmp(name_hashtable[state_status], "config-files") == 0 + ) { + bb_error_msg_and_die("%s is already removed", name_hashtable[package_hashtable[package_num]->name]); + } + set_status(status_num, "deinstall", 1); + } else if (opt & OPT_purge) { + /* if package status is "conf-files" then its ok */ + if (strcmp(name_hashtable[state_status], "not-installed") == 0) { + bb_error_msg_and_die("%s is already purged", name_hashtable[package_hashtable[package_num]->name]); + } + set_status(status_num, "purge", 1); + } + } + deb_count++; + argv++; + } + if (!deb_count) + bb_error_msg_and_die("no package files specified"); + deb_file[deb_count] = NULL; + + /* Check that the deb file arguments are installable */ + if (!(opt & OPT_force_ignore_depends)) { + if (!check_deps(deb_file, 0 /*, deb_count*/)) { + bb_error_msg_and_die("dependency check failed"); + } + } + + /* TODO: install or remove packages in the correct dependency order */ + for (i = 0; i < deb_count; i++) { + /* Remove or purge packages */ + if (opt & OPT_remove) { + remove_package(deb_file[i]->package, 1); + } + else if (opt & OPT_purge) { + purge_package(deb_file[i]->package); + } + else if (opt & OPT_unpack) { + unpack_package(deb_file[i]); + } + else if (opt & OPT_install) { + unpack_package(deb_file[i]); + /* package is configured in second pass below */ + } + else if (opt & OPT_configure) { + configure_package(deb_file[i]); + } + } + /* configure installed packages */ + if (opt & OPT_install) { + for (i = 0; i < deb_count; i++) + configure_package(deb_file[i]); + } + + write_status_file(deb_file); + + if (ENABLE_FEATURE_CLEAN_UP) { + for (i = 0; i < deb_count; i++) { + free(deb_file[i]->control_file); + free(deb_file[i]->filename); + free(deb_file[i]); + } + + free(deb_file); + + for (i = 0; i < NAME_HASH_PRIME; i++) { + free(name_hashtable[i]); + } + + for (i = 0; i < PACKAGE_HASH_PRIME; i++) { + free_package(package_hashtable[i]); + } + + for (i = 0; i < STATUS_HASH_PRIME; i++) { + free(status_hashtable[i]); + } + } + + return EXIT_SUCCESS; +} diff --git a/busybox/archival/dpkg_deb.c b/busybox/archival/dpkg_deb.c new file mode 100644 index 00000000000000..dc4738d28ec686 --- /dev/null +++ b/busybox/archival/dpkg_deb.c @@ -0,0 +1,134 @@ +/* vi: set sw=4 ts=4: */ +/* + * dpkg-deb packs, unpacks and provides information about Debian archives. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config DPKG_DEB +//config: bool "dpkg_deb" +//config: default y +//config: select FEATURE_SEAMLESS_GZ +//config: help +//config: dpkg-deb unpacks and provides information about Debian archives. +//config: +//config: This implementation of dpkg-deb cannot pack archives. +//config: +//config: Unless you have a specific application which requires dpkg-deb, +//config: say N here. + +//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 [DIR]" +//usage:#define dpkg_deb_full_usage "\n\n" +//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" + +#include "libbb.h" +#include "bb_archive.h" + +#define DPKG_DEB_OPT_CONTENTS 1 +#define DPKG_DEB_OPT_CONTROL 2 +#define DPKG_DEB_OPT_FIELD 4 +#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 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; + + /* Setup the tar archive handle */ + tar_archive = init_handle(); + + /* Setup an ar archive handle that refers to the gzip sub archive */ + ar_archive = init_handle(); + 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"); +#endif +#if ENABLE_FEATURE_SEAMLESS_BZ2 + llist_add_to(&ar_archive->accept, (char*)"data.tar.bz2"); + llist_add_to(&control_tar_llist, (char*)"control.tar.bz2"); +#endif +#if ENABLE_FEATURE_SEAMLESS_LZMA + 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 + + /* Must have 1 or 2 args */ + opt = getopt32(argv, "^" "cefXx" + "\0" "-1:?2:c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX" + ); + argv += optind; + //argc -= optind; + + extract_dir = argv[1]; + if (opt & DPKG_DEB_OPT_CONTENTS) { // -c + tar_archive->action_header = header_verbose_list; + if (extract_dir) + bb_show_usage(); + } + 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_CONTROL) { // -e + ar_archive->accept = control_tar_llist; + tar_archive->action_data = data_extract_all; + if (!extract_dir) + extract_dir = "./DEBIAN"; + } + 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(); + } + + /* Standard tool supports "-" */ + tar_archive->src_fd = ar_archive->src_fd = xopen_stdin(argv[0]); + + if (extract_dir) { + mkdir(extract_dir, 0777); /* bb_make_directory(extract_dir, 0777, 0) */ + xchdir(extract_dir); + } + + /* Do it */ + unpack_ar_archive(ar_archive); + + /* Cleanup */ + if (ENABLE_FEATURE_CLEAN_UP) + close(ar_archive->src_fd); + + return EXIT_SUCCESS; +} diff --git a/busybox/archival/gzip.c b/busybox/archival/gzip.c new file mode 100644 index 00000000000000..c5a1fe9b4ec255 --- /dev/null +++ b/busybox/archival/gzip.c @@ -0,0 +1,2246 @@ +/* vi: set sw=4 ts=4: */ +/* + * Gzip implementation for busybox + * + * Based on GNU gzip Copyright (C) 1992-1993 Jean-loup Gailly. + * + * Originally adjusted for busybox by Charles P. Wright + * "this is a stripped down version of gzip I put into busybox, it does + * only standard in to standard out with -9 compression. It also requires + * the zcat module for some important functions." + * + * Adjusted further by Erik Andersen to support + * files as well as stdin/stdout, and to generally behave itself wrt + * command line handling. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* TODO: full support for -v for DESKTOP + * "/usr/bin/gzip -v a bogus aa" should say: +a: 85.1% -- replaced with a.gz +gzip: bogus: No such file or directory +aa: 85.1% -- replaced with aa.gz +*/ +//config:config GZIP +//config: bool "gzip (19 kb)" +//config: default y +//config: help +//config: gzip is used to compress files. +//config: It's probably the most widely used UNIX compression program. +//config: +//config:config FEATURE_GZIP_LONG_OPTIONS +//config: bool "Enable long options" +//config: default y +//config: depends on GZIP && LONG_OPTS +//config: +//config:config GZIP_FAST +//config: int "Trade memory for speed (0:small,slow - 2:fast,big)" +//config: default 0 +//config: range 0 2 +//config: depends on GZIP +//config: help +//config: Enable big memory options for gzip. +//config: 0: small buffers, small hash-tables +//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. +//config: +//config:config FEATURE_GZIP_DECOMPRESS +//config: bool "Enable decompression" +//config: default y +//config: depends on GZIP || GUNZIP || ZCAT +//config: help +//config: Enable -d (--decompress) and -t (--test) options for gzip. +//config: This will be automatically selected if gunzip or zcat is +//config: enabled. + +//applet:IF_GZIP(APPLET(gzip, BB_DIR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_GZIP) += gzip.o + +//usage:#define gzip_trivial_usage +//usage: "[-cfk" IF_FEATURE_GZIP_DECOMPRESS("dt") 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_FEATURE_GZIP_DECOMPRESS( +//usage: "\n -d Decompress" +//usage: "\n -t Test file integrity" +//usage: ) +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -k Keep input files" +//usage: +//usage:#define gzip_example_usage +//usage: "$ ls -la /tmp/busybox*\n" +//usage: "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/busybox.tar\n" +//usage: "$ gzip /tmp/busybox.tar\n" +//usage: "$ ls -la /tmp/busybox*\n" +//usage: "-rw-rw-r-- 1 andersen andersen 554058 Apr 14 17:49 /tmp/busybox.tar.gz\n" + +#include "libbb.h" +#include "bb_archive.h" + +/* =========================================================================== + */ +//#define DEBUG 1 +/* Diagnostic functions */ +#ifdef DEBUG +static int verbose; +# define Assert(cond,msg) { if (!(cond)) bb_error_msg(msg); } +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x; } +# define Tracevv(x) {if (verbose > 1) fprintf x; } +# define Tracec(c,x) {if (verbose && (c)) fprintf x; } +# define Tracecv(c,x) {if (verbose > 1 && (c)) fprintf x; } +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +/* =========================================================================== + */ +#if CONFIG_GZIP_FAST == 0 +# define SMALL_MEM +#elif CONFIG_GZIP_FAST == 1 +# define MEDIUM_MEM +#elif CONFIG_GZIP_FAST == 2 +# define BIG_MEM +#else +# error "Invalid CONFIG_GZIP_FAST value" +#endif + +#ifndef INBUFSIZ +# ifdef SMALL_MEM +# define INBUFSIZ 0x2000 /* input buffer size */ +# else +# define INBUFSIZ 0x8000 /* input buffer size */ +# endif +#endif + +#ifndef OUTBUFSIZ +# ifdef SMALL_MEM +# define OUTBUFSIZ 8192 /* output buffer size */ +# else +# define OUTBUFSIZ 16384 /* output buffer size */ +# endif +#endif + +#ifndef DIST_BUFSIZE +# ifdef SMALL_MEM +# define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */ +# else +# define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */ +# endif +#endif + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* internal file attribute */ +#define UNKNOWN 0xffff +#define BINARY 0 +#define ASCII 1 + +#ifndef WSIZE +# define WSIZE 0x8000 /* window size--must be a power of two, and */ +#endif /* at least 32K for zip's deflate method */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#ifndef MAX_PATH_LEN +# define MAX_PATH_LEN 1024 /* max pathname length */ +#endif + +#define seekable() 0 /* force sequential output */ +#define translate_eol 0 /* no option -a yet */ + +#ifndef BITS +# define BITS 16 +#endif +#define INIT_BITS 9 /* Initial number of bits per code */ + +#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */ +/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free. + * It's a pity that old uncompress does not check bit 0x20. That makes + * extension of the format actually undesirable because old compress + * would just crash on the new format instead of giving a meaningful + * error message. It does check the number of bits, but it's more + * helpful to say "unsupported format, get a new version" than + * "can only handle 16 bits". + */ + +#ifdef MAX_EXT_CHARS +# define MAX_SUFFIX MAX_EXT_CHARS +#else +# define MAX_SUFFIX 30 +#endif + +/* =========================================================================== + * Compile with MEDIUM_MEM to reduce the memory requirements or + * with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the + * entire input file can be held in memory (not possible on 16 bit systems). + * Warning: defining these symbols affects HASH_BITS (see below) and thus + * affects the compression ratio. The compressed output + * is still correct, and might even be smaller in some cases. + */ +#ifdef SMALL_MEM +# define HASH_BITS 13 /* Number of bits used to hash strings */ +#endif +#ifdef MEDIUM_MEM +# define HASH_BITS 14 +#endif +#ifndef HASH_BITS +# define HASH_BITS 15 + /* For portability to 16 bit machines, do not use values above 15. */ +#endif + +#define HASH_SIZE (unsigned)(1<= 4. + */ + + max_insert_length = max_lazy_match, +/* Insert new strings in the hash table only if the match length + * is not greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + good_match = 32, +/* Use a faster search when the previous match is longer than this */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ + + nice_match = 258, /* Stop searching when current match exceeds this */ +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ +#endif /* ENABLE_FEATURE_GZIP_LEVELS */ +}; + +struct globals { +/* =========================================================================== */ +/* global buffers, allocated once */ + +#define DECLARE(type, array, size) \ + type * array +#define ALLOC(type, array, size) \ + array = xzalloc((size_t)(((size)+1L)/2) * 2*sizeof(type)) +#define FREE(array) \ + do { free(array); array = NULL; } while (0) + + /* buffer for literals or lengths */ + /* DECLARE(uch, l_buf, LIT_BUFSIZE); */ + DECLARE(uch, l_buf, INBUFSIZ); + + DECLARE(ush, d_buf, DIST_BUFSIZE); + DECLARE(uch, outbuf, OUTBUFSIZ); + +/* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least WSIZE + * bytes. With this organization, matches are limited to a distance of + * WSIZE-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: limit the window size to WSIZE+BSZ if SMALL_MEM (the code would + * be less efficient). + */ + DECLARE(uch, window, 2L * WSIZE); + +/* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + /* DECLARE(Pos, prev, WSIZE); */ + DECLARE(ush, prev, 1L << BITS); + +/* Heads of the hash chains or 0. */ + /* DECLARE(Pos, head, 1<= HASH_BITS + */ +#define H_SHIFT ((HASH_BITS+MIN_MATCH-1) / MIN_MATCH) + +/* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + unsigned prev_length; + + unsigned strstart; /* start of string to insert */ + unsigned match_start; /* start of matching string */ + unsigned lookahead; /* number of valid bytes ahead in window */ + +/* number of input bytes */ + ulg isize; /* only 32 bits stored in .gz file */ + +/* bbox always use stdin/stdout */ +#define ifd STDIN_FILENO /* input file descriptor */ +#define ofd STDOUT_FILENO /* output file descriptor */ + +#ifdef DEBUG + unsigned insize; /* valid bytes in l_buf */ +#endif + unsigned outcnt; /* bytes in output buffer */ + smallint eofile; /* flag set at end of input file */ + +/* =========================================================================== + * Local data used by the "bit string" routines. + */ + +/* Output buffer. bits are inserted starting at the bottom (least significant + * bits). + */ + unsigned bi_buf; /* was unsigned short */ + +#undef BUF_SIZE +#define BUF_SIZE (int)(8 * sizeof(G1.bi_buf)) + +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + unsigned bi_valid; + +#ifdef DEBUG + ulg bits_sent; /* bit length of the compressed data */ +# define DEBUG_bits_sent(v) (void)(G1.bits_sent v) +#else +# define DEBUG_bits_sent(v) ((void)0) +#endif +}; + +#define G1 (*(ptr_to_globals - 1)) + +/* =========================================================================== + * Write the output buffer outbuf[0..outcnt-1] and update bytes_out. + * (used for the compressed data only) + */ +static void flush_outbuf(void) +{ + if (G1.outcnt == 0) + return; + + xwrite(ofd, (char *) G1.outbuf, G1.outcnt); + G1.outcnt = 0; +} + +/* =========================================================================== + */ +/* put_8bit is used for the compressed output */ +#define put_8bit(c) \ +do { \ + G1.outbuf[G1.outcnt++] = (c); \ + if (G1.outcnt == OUTBUFSIZ) \ + flush_outbuf(); \ +} while (0) + +/* Output a 16 bit value, lsb first */ +static void put_16bit(ush w) +{ + /* 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; /* unaligned LSB 16-bit store */ + G1.outcnt = outcnt + 2; + return; + } + *dst = (uch)w; + w >>= 8; + G1.outcnt = ++outcnt; +#else + *dst = (uch)w; + w >>= 8; + if (outcnt < OUTBUFSIZ-2) { + /* Common case */ + dst[1] = w; + G1.outcnt = outcnt + 2; + return; + } + G1.outcnt = ++outcnt; +#endif + + /* Slowpath: we will need to do flush_outbuf() */ + if (outcnt == OUTBUFSIZ) + flush_outbuf(); /* here */ + put_8bit(w); /* or here */ +} + +#define OPTIMIZED_PUT_32BIT (CONFIG_GZIP_FAST > 0 && BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN) +static void put_32bit(ulg n) +{ + if (OPTIMIZED_PUT_32BIT) { + unsigned outcnt = G1.outcnt; + if (outcnt < OUTBUFSIZ-4) { + /* Common case */ + ulg *dst32 = (void*) &G1.outbuf[outcnt]; + *dst32 = n; /* unaligned LSB 32-bit store */ + //bb_error_msg("%p", dst32); // store alignment debugging + G1.outcnt = outcnt + 4; + return; + } + } + put_16bit(n); + put_16bit(n >> 16); +} +static ALWAYS_INLINE void flush_outbuf_if_32bit_optimized(void) +{ + /* If put_32bit() performs 32bit stores && it is used in send_bits() */ + if (OPTIMIZED_PUT_32BIT && BUF_SIZE > 16) + flush_outbuf(); +} + +/* =========================================================================== + * Run a set of bytes through the crc shift register. If s is a NULL + * pointer, then initialize the crc shift register contents instead. + * Return the current crc in either case. + */ +static void updcrc(uch * s, unsigned n) +{ + G1.crc = crc32_block_endian0(G1.crc, s, n, global_crc32_table /*G1.crc_32_tab*/); +} + +/* =========================================================================== + * Read a new buffer from the current input file, perform end-of-line + * translation, and update the crc and input file size. + * IN assertion: size >= 2 (for end-of-line translation) + */ +static unsigned file_read(void *buf, unsigned size) +{ + unsigned len; + + Assert(G1.insize == 0, "l_buf not empty"); + + len = safe_read(ifd, buf, size); + if (len == (unsigned)(-1) || len == 0) + return len; + + updcrc(buf, len); + G1.isize += len; + return len; +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +static void send_bits(unsigned value, unsigned length) +{ + unsigned new_buf; + +#ifdef DEBUG + Tracev((stderr, " l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + DEBUG_bits_sent(+= length); +#endif + BUILD_BUG_ON(BUF_SIZE != 32 && BUF_SIZE != 16); + + new_buf = G1.bi_buf | (value << G1.bi_valid); + /* NB: the above may sometimes do "<< 32" shift (undefined) + * if check below is changed to "length > BUF_SIZE" instead of >= */ + length += G1.bi_valid; + + /* If bi_buf is full */ + if (length >= BUF_SIZE) { + /* ...use (valid) bits from bi_buf and + * (BUF_SIZE - bi_valid) bits from value, + * leaving (width - (BUF_SIZE-bi_valid)) unused bits in value. + */ + value >>= (BUF_SIZE - G1.bi_valid); + if (BUF_SIZE == 32) { + put_32bit(new_buf); + } else { /* 16 */ + put_16bit(new_buf); + } + new_buf = value; + length -= BUF_SIZE; + } + G1.bi_buf = new_buf; + G1.bi_valid = length; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +static unsigned bi_reverse(unsigned code, int len) +{ + unsigned res = 0; + + while (1) { + res |= code & 1; + if (--len <= 0) return res; + code >>= 1; + res <<= 1; + } +} + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +static void bi_windup(void) +{ + unsigned bits = G1.bi_buf; + int cnt = G1.bi_valid; + + while (cnt > 0) { + put_8bit(bits); + bits >>= 8; + cnt -= 8; + } + G1.bi_buf = 0; + G1.bi_valid = 0; + DEBUG_bits_sent(= (G1.bits_sent + 7) & ~7); +} + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +static void copy_block(char *buf, unsigned len, int header) +{ + bi_windup(); /* align on byte boundary */ + + if (header) { + unsigned v = ((uint16_t)len) | ((~len) << 16); + put_32bit(v); + DEBUG_bits_sent(+= 2 * 16); + } + DEBUG_bits_sent(+= (ulg) len << 3); + while (len--) { + put_8bit(*buf++); + } + /* The above can 32-bit misalign outbuf */ + if (G1.outcnt & 3) /* syscalls are expensive, is it really misaligned? */ + flush_outbuf_if_32bit_optimized(); +} + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: at least one byte has been read, or eofile is set; + * file reads are performed for at least two bytes (required for the + * translate_eol option). + */ +static void fill_window(void) +{ + unsigned n, m; + unsigned more = WINDOW_SIZE - G1.lookahead - G1.strstart; + /* Amount of free space at the end of the window. */ + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned) -1) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + } else if (G1.strstart >= WSIZE + MAX_DIST) { + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + Assert(WINDOW_SIZE == 2 * WSIZE, "no sliding with BIG_MEM"); + + memcpy(G1.window, G1.window + WSIZE, WSIZE); + G1.match_start -= WSIZE; + G1.strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + G1.block_start -= WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = head[n]; + head[n] = (Pos) (m >= WSIZE ? m - WSIZE : 0); + } + for (n = 0; n < WSIZE; n++) { + m = G1.prev[n]; + G1.prev[n] = (Pos) (m >= WSIZE ? m - WSIZE : 0); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + /* At this point, more >= 2 */ + if (!G1.eofile) { + n = file_read(G1.window + G1.strstart + G1.lookahead, more); + if (n == 0 || n == (unsigned) -1) { + G1.eofile = 1; + } else { + G1.lookahead += n; + } + } +} +/* Both users fill window with the same loop: */ +static void fill_window_if_needed(void) +{ + while (G1.lookahead < MIN_LOOKAHEAD && !G1.eofile) + fill_window(); +} + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + */ + +/* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or + * match.s. The code is functionally equivalent, so you can use the C version + * if desired. + */ +static int longest_match(IPos cur_match) +{ + unsigned chain_length = max_chain_length; /* max hash chain length */ + uch *scan = G1.window + G1.strstart; /* current string */ + uch *match; /* matched string */ + int len; /* length of current match */ + int best_len = G1.prev_length; /* best match length so far */ + IPos limit = G1.strstart > (IPos) MAX_DIST ? G1.strstart - (IPos) MAX_DIST : 0; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + +/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ +#if HASH_BITS < 8 || MAX_MATCH != 258 +# error Code too clever +#endif + uch *strend = G1.window + G1.strstart + MAX_MATCH; + uch scan_end1 = scan[best_len - 1]; + uch scan_end = scan[best_len]; + + /* Do not waste too much time if we already have a good match: */ + if (G1.prev_length >= good_match) { + chain_length >>= 2; + } + Assert(G1.strstart <= WINDOW_SIZE - MIN_LOOKAHEAD, "insufficient lookahead"); + + do { + Assert(cur_match < G1.strstart, "no future"); + match = G1.window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + if (match[best_len] != scan_end + || match[best_len - 1] != scan_end1 + || *match != *scan || *++match != scan[1] + ) { + continue; + } + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && scan < strend); + + len = MAX_MATCH - (int) (strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) { + G1.match_start = cur_match; + best_len = len; + if (len >= nice_match) + break; + scan_end1 = scan[best_len - 1]; + scan_end = scan[best_len]; + } + } while ((cur_match = G1.prev[cur_match & WMASK]) > limit + && --chain_length != 0); + + return best_len; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +static void check_match(IPos start, IPos match, int length) +{ + /* check that the match is indeed a match */ + if (memcmp(G1.window + match, G1.window + start, length) != 0) { + bb_error_msg(" start %d, match %d, length %d", start, match, length); + bb_error_msg("invalid match"); + } + if (verbose > 1) { + bb_error_msg("\\[%d,%d]", start - match, length); + do { + bb_putchar_stderr(G1.window[start++]); + } while (--length != 0); + } +} +#else +# define check_match(start, match, length) ((void)0) +#endif + + +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* PURPOSE + * Encode various sets of source values using variable-length + * binary code trees. + * + * DISCUSSION + * The PKZIP "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in the ZIP file in a compressed form + * which is itself a Huffman encoding of the lengths of + * all the code strings (in ascending order by source values). + * The actual code strings are reconstructed from the lengths in + * the UNZIP process, as described in the "application note" + * (APPNOTE.TXT) distributed as part of PKWARE's PKZIP program. + * + * REFERENCES + * Lynch, Thomas J. + * Data Compression: Techniques and Applications, pp. 53-55. + * Lifetime Learning Publications, 1985. ISBN 0-534-03418-7. + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + * + * INTERFACE + * void ct_init() + * Allocate the match buffer, initialize the various tables [and save + * the location of the internal file attribute (ascii/binary) and + * method (DEFLATE/STORE) -- deleted in bbox] + * + * void ct_tally(int dist, int lc); + * Save the match info and tally the frequency counts. + * + * ulg flush_block(char *buf, ulg stored_len, int eof) + * Determine the best encoding for the current block: dynamic trees, + * static trees or store, and output the encoded block to the zip + * file. Returns the total compressed length for the file so far. + */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +/* extra bits for each length code */ +static const uint8_t extra_lbits[LENGTH_CODES] ALIGN1 = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, + 4, 4, 5, 5, 5, 5, 0 +}; + +/* extra bits for each distance code */ +static const uint8_t extra_dbits[D_CODES] ALIGN1 = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, + 10, 10, 11, 11, 12, 12, 13, 13 +}; + +/* extra bits for each bit length code */ +static const uint8_t extra_blbits[BL_CODES] ALIGN1 = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 }; + +/* number of codes at each bit length for an optimal tree */ +static const uint8_t bl_order[BL_CODES] ALIGN1 = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#ifndef LIT_BUFSIZE +# ifdef SMALL_MEM +# define LIT_BUFSIZE 0x2000 +# else +# ifdef MEDIUM_MEM +# define LIT_BUFSIZE 0x4000 +# else +# define LIT_BUFSIZE 0x8000 +# endif +# endif +#endif +#ifndef DIST_BUFSIZE +# define DIST_BUFSIZE LIT_BUFSIZE +#endif +/* Sizes of match buffers for literals/lengths and distances. There are + * 4 reasons for limiting LIT_BUFSIZE to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input data is + * still in the window so we can still emit a stored block even when input + * comes from standard input. (This can also be done for all blocks if + * LIT_BUFSIZE is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting trees + * more frequently. + * - I can't count above 4 + * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save + * memory at the expense of compression). Some optimizations would be possible + * if we rely on DIST_BUFSIZE == LIT_BUFSIZE. + */ +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +/* =========================================================================== +*/ +/* Data structure describing a single value and its code string. */ +typedef struct ct_data { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +#define HEAP_SIZE (2*L_CODES + 1) +/* maximum heap size */ + +typedef struct tree_desc { + ct_data *dyn_tree; /* the dynamic tree */ + ct_data *static_tree; /* corresponding static tree or NULL */ + const uint8_t *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ + int max_code; /* largest code with non zero frequency */ +} tree_desc; + +struct globals2 { + + ush heap[HEAP_SIZE]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + +/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + ct_data dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + ct_data dyn_dtree[2 * D_CODES + 1]; /* distance tree */ + + ct_data static_ltree[L_CODES + 2]; + +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see ct_init + * below). + */ + + ct_data static_dtree[D_CODES]; + +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + + ct_data bl_tree[2 * BL_CODES + 1]; + +/* Huffman tree for the bit lengths */ + + tree_desc l_desc; + tree_desc d_desc; + tree_desc bl_desc; + + ush bl_count[MAX_BITS + 1]; + +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + + uch depth[2 * L_CODES + 1]; + +/* Depth of each subtree used as tie breaker for trees of equal frequency */ + + uch length_code[MAX_MATCH - MIN_MATCH + 1]; + +/* length code for each normalized match length (0 == MIN_MATCH) */ + + uch dist_code[512]; + +/* distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + + int base_length[LENGTH_CODES]; + +/* First normalized length for each code (0 = MIN_MATCH) */ + + int base_dist[D_CODES]; + +/* First normalized distance for each code (0 = distance of 1) */ + + uch flag_buf[LIT_BUFSIZE / 8]; + +/* flag_buf is a bit array distinguishing literals from lengths in + * l_buf, thus indicating the presence or absence of a distance. + */ + + unsigned last_lit; /* running index in l_buf */ + unsigned last_dist; /* running index in d_buf */ + unsigned last_flags; /* running index in flag_buf */ + uch flags; /* current flags not yet saved in flag_buf */ + uch flag_bit; /* current bit used in flags */ + +/* bits are filled in flags starting at bit 0 (least significant). + * Note: these flags are overkill in the current code since we don't + * take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + +// ulg compressed_len; /* total bit length of compressed file */ +}; + +#define G2ptr ((struct globals2*)(ptr_to_globals)) +#define G2 (*G2ptr) + +/* =========================================================================== + */ +#ifndef DEBUG +/* Send a code of the given tree. c and tree must not have side effects */ +# define SEND_CODE(c, tree) send_bits(tree[c].Code, tree[c].Len) +#else +# define SEND_CODE(c, tree) \ +{ \ + if (verbose > 1) bb_error_msg("\ncd %3d ", (c)); \ + send_bits(tree[c].Code, tree[c].Len); \ +} +#endif + +#define D_CODE(dist) \ + ((dist) < 256 ? G2.dist_code[dist] : G2.dist_code[256 + ((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + * The arguments must not have side effects. + */ + +/* =========================================================================== + * Initialize a new block. + */ +static void init_block(void) +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) + G2.dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) + G2.dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) + G2.bl_tree[n].Freq = 0; + + G2.dyn_ltree[END_BLOCK].Freq = 1; + G2.opt_len = G2.static_len = 0; + G2.last_lit = G2.last_dist = G2.last_flags = 0; + G2.flags = 0; + G2.flag_bit = 1; +} + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ + +/* Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. */ +#define SMALLER(tree, n, m) \ + (tree[n].Freq < tree[m].Freq \ + || (tree[n].Freq == tree[m].Freq && G2.depth[n] <= G2.depth[m])) + +static void pqdownheap(ct_data * tree, int k) +{ + int v = G2.heap[k]; + int j = k << 1; /* left son of k */ + + while (j <= G2.heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < G2.heap_len && SMALLER(tree, G2.heap[j + 1], G2.heap[j])) + j++; + + /* Exit if v is smaller than both sons */ + if (SMALLER(tree, v, G2.heap[j])) + break; + + /* Exchange v with the smallest son */ + G2.heap[k] = G2.heap[j]; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + G2.heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +static void gen_bitlen(tree_desc * desc) +{ + ct_data *tree = desc->dyn_tree; + const uint8_t *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) + G2.bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[G2.heap[G2.heap_max]].Len = 0; /* root of the heap */ + + for (h = G2.heap_max + 1; h < HEAP_SIZE; h++) { + n = G2.heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) { + bits = max_length; + overflow++; + } + tree[n].Len = (ush) bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) + continue; /* not a leaf node */ + + G2.bl_count[bits]++; + xbits = 0; + if (n >= base) + xbits = extra[n - base]; + f = tree[n].Freq; + G2.opt_len += (ulg) f *(bits + xbits); + + if (stree) + G2.static_len += (ulg) f * (stree[n].Len + xbits); + } + if (overflow == 0) + return; + + Trace((stderr, "\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length - 1; + while (G2.bl_count[bits] == 0) + bits--; + G2.bl_count[bits]--; /* move one leaf down the tree */ + G2.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ + G2.bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = G2.bl_count[bits]; + while (n != 0) { + m = G2.heap[--h]; + if (m > max_code) + continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr, "code %d bits %d->%d\n", m, tree[m].Len, bits)); + G2.opt_len += ((int32_t) bits - tree[m].Len) * tree[m].Freq; + tree[m].Len = bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +static void gen_codes(ct_data * tree, int max_code) +{ + ush next_code[MAX_BITS + 1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + G2.bl_count[bits - 1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert(code + G2.bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1, + "inconsistent bit counts"); + Tracev((stderr, "\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + + if (len == 0) + continue; + /* Now reverse the bits */ + tree[n].Code = bi_reverse(next_code[len]++, len); + + Tracec(tree != G2.static_ltree, + (stderr, "\nn %3d %c l %2d c %4x (%x) ", n, + (n > ' ' ? n : ' '), len, tree[n].Code, + next_code[len] - 1)); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ + +/* Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. */ + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + +#define PQREMOVE(tree, top) \ +do { \ + top = G2.heap[SMALLEST]; \ + G2.heap[SMALLEST] = G2.heap[G2.heap_len--]; \ + pqdownheap(tree, SMALLEST); \ +} while (0) + +static void build_tree(tree_desc * desc) +{ + ct_data *tree = desc->dyn_tree; + ct_data *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + G2.heap_len = 0; + G2.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + G2.heap[++G2.heap_len] = max_code = n; + G2.depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (G2.heap_len < 2) { + int new = G2.heap[++G2.heap_len] = (max_code < 2 ? ++max_code : 0); + + tree[new].Freq = 1; + G2.depth[new] = 0; + G2.opt_len--; + if (stree) + G2.static_len -= stree[new].Len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = G2.heap_len / 2; n >= 1; n--) + pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + PQREMOVE(tree, n); /* n = node of least frequency */ + m = G2.heap[SMALLEST]; /* m = node of next least frequency */ + + G2.heap[--G2.heap_max] = n; /* keep the nodes sorted by frequency */ + G2.heap[--G2.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + G2.depth[node] = MAX(G2.depth[n], G2.depth[m]) + 1; + tree[n].Dad = tree[m].Dad = (ush) node; +#ifdef DUMP_BL_TREE + if (tree == G2.bl_tree) { + bb_error_msg("\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* 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]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen((tree_desc *) desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes((ct_data *) tree, max_code); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +static void scan_tree(ct_data * tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + tree[max_code + 1].Len = 0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].Len; + if (++count < max_count && curlen == nextlen) + continue; + + if (count < min_count) { + G2.bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) + G2.bl_tree[curlen].Freq++; + G2.bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + G2.bl_tree[REPZ_3_10].Freq++; + } else { + G2.bl_tree[REPZ_11_138].Freq++; + } + count = 0; + prevlen = curlen; + + max_count = 7; + min_count = 4; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +static void send_tree(ct_data * tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + +/* tree[max_code+1].Len = -1; *//* guard already set */ + if (nextlen == 0) + max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[n + 1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { + SEND_CODE(curlen, G2.bl_tree); + } while (--count); + } else if (curlen != 0) { + if (curlen != prevlen) { + SEND_CODE(curlen, G2.bl_tree); + count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + SEND_CODE(REP_3_6, G2.bl_tree); + send_bits(count - 3, 2); + } else if (count <= 10) { + SEND_CODE(REPZ_3_10, G2.bl_tree); + send_bits(count - 3, 3); + } else { + SEND_CODE(REPZ_11_138, G2.bl_tree); + send_bits(count - 11, 7); + } + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +static int build_bl_tree(void) +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(G2.dyn_ltree, G2.l_desc.max_code); + scan_tree(G2.dyn_dtree, G2.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(&G2.bl_desc); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { + if (G2.bl_tree[bl_order[max_blindex]].Len != 0) + break; + } + /* Update opt_len to include the bit length tree and counts */ + G2.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", (long)G2.opt_len, (long)G2.static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +static void send_all_trees(int lcodes, int dcodes, int blcodes) +{ + int rank; /* index in bl_order */ + + Assert(lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert(lcodes <= L_CODES && dcodes <= D_CODES + && blcodes <= BL_CODES, "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(lcodes - 257, 5); /* not +255 as stated in appnote.txt */ + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(G2.bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", (long)G1.bits_sent)); + + send_tree((ct_data *) G2.dyn_ltree, lcodes - 1); /* send the literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", (long)G1.bits_sent)); + + send_tree((ct_data *) G2.dyn_dtree, dcodes - 1); /* send the distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", (long)G1.bits_sent)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +static int ct_tally(int dist, int lc) +{ + G1.l_buf[G2.last_lit++] = lc; + if (dist == 0) { + /* lc is the unmatched char */ + G2.dyn_ltree[lc].Freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush) dist < (ush) MAX_DIST + && (ush) lc <= (ush) (MAX_MATCH - MIN_MATCH) + && (ush) D_CODE(dist) < (ush) D_CODES, "ct_tally: bad match" + ); + + G2.dyn_ltree[G2.length_code[lc] + LITERALS + 1].Freq++; + G2.dyn_dtree[D_CODE(dist)].Freq++; + + G1.d_buf[G2.last_dist++] = dist; + G2.flags |= G2.flag_bit; + } + G2.flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((G2.last_lit & 7) == 0) { + G2.flag_buf[G2.last_flags++] = G2.flags; + G2.flags = 0; + G2.flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if ((G2.last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = G2.last_lit * 8L; + ulg in_length = (ulg) G1.strstart - G1.block_start; + int dcode; + + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += G2.dyn_dtree[dcode].Freq * (5L + extra_dbits[dcode]); + } + out_length >>= 3; + Trace((stderr, + "\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + G2.last_lit, G2.last_dist, + (long)in_length, (long)out_length, + 100L - out_length * 100L / in_length)); + if (G2.last_dist < G2.last_lit / 2 && out_length < in_length / 2) + return 1; + } + return (G2.last_lit == LIT_BUFSIZE - 1 || G2.last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +static void compress_block(ct_data * ltree, ct_data * dtree) +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (G2.last_lit != 0) do { + if ((lx & 7) == 0) + flag = G2.flag_buf[fx++]; + lc = G1.l_buf[lx++]; + if ((flag & 1) == 0) { + SEND_CODE(lc, ltree); /* send a literal byte */ + Tracecv(lc > ' ', (stderr, " '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = G2.length_code[lc]; + SEND_CODE(code + LITERALS + 1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= G2.base_length[code]; + send_bits(lc, extra); /* send the extra length bits */ + } + dist = G1.d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = D_CODE(dist); + Assert(code < D_CODES, "bad d_code"); + + SEND_CODE(code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= G2.base_dist[code]; + send_bits(dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < G2.last_lit); + + SEND_CODE(END_BLOCK, ltree); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length for the file so far. + */ +static void flush_block(char *buf, ulg stored_len, int eof) +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + G2.flag_buf[G2.last_flags] = G2.flags; /* Save the flags for the last 8 items */ + + /* Construct the literal and distance trees */ + build_tree(&G2.l_desc); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", (long)G2.opt_len, (long)G2.static_len)); + + build_tree(&G2.d_desc); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", (long)G2.opt_len, (long)G2.static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (G2.opt_len + 3 + 7) >> 3; + static_lenb = (G2.static_len + 3 + 7) >> 3; + + Trace((stderr, + "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + (unsigned long)opt_lenb, (unsigned long)G2.opt_len, + (unsigned long)static_lenb, (unsigned long)G2.static_len, + (unsigned long)stored_len, + G2.last_lit, G2.last_dist)); + + if (static_lenb <= opt_lenb) + opt_lenb = static_lenb; + + /* If compression failed and this is the first and last block, + * and if the zip file can be seeked (to rewrite the local header), + * the whole file is transformed into a stored file: + */ +// seekable() is constant FALSE in busybox, and G2.compressed_len is disabled +// (this was the only user) +// if (stored_len <= opt_lenb && eof && G2.compressed_len == 0L && seekable()) { +// /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ +// if (buf == NULL) +// bb_error_msg("block vanished"); +// +// G2.compressed_len = stored_len << 3; +// copy_block(buf, (unsigned) stored_len, 0); /* without header */ +// } 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. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits((STORED_BLOCK << 1) + eof, 3); /* send block type */ +// G2.compressed_len = ((G2.compressed_len + 3 + 7) & ~7L) +// + ((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); +// G2.compressed_len += 3 + G2.static_len; + } else { + send_bits((DYN_TREES << 1) + eof, 3); + send_all_trees(G2.l_desc.max_code + 1, G2.d_desc.max_code + 1, + max_blindex + 1); + compress_block((ct_data *) G2.dyn_ltree, (ct_data *) G2.dyn_dtree); +// G2.compressed_len += 3 + G2.opt_len; + } +// Assert(G2.compressed_len == G1.bits_sent, "bad compressed size"); + init_block(); + + if (eof) { + bi_windup(); +// G2.compressed_len += 7; /* align on byte boundary */ + } +// Tracev((stderr, "\ncomprlen %lu(%lu) ", +// (unsigned long)G2.compressed_len >> 3, +// (unsigned long)G2.compressed_len - 7 * eof)); + + return; /* was "return G2.compressed_len >> 3;" */ +} + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(h, c) (h = (((h)<= 0L \ + ? (char*)&G1.window[(unsigned)G1.block_start] \ + : (char*)NULL, \ + (ulg)G1.strstart - G1.block_start, \ + (eof) \ + ) + +/* Insert string s in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * IN assertion: all calls to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of s are valid + * (except for the last MIN_MATCH-1 bytes of the input file). */ +#define INSERT_STRING(s, match_head) \ +do { \ + UPDATE_HASH(G1.ins_h, G1.window[(s) + MIN_MATCH-1]); \ + G1.prev[(s) & WMASK] = match_head = head[G1.ins_h]; \ + head[G1.ins_h] = (s); \ +} while (0) + +static NOINLINE void deflate(void) +{ + IPos hash_head; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + unsigned match_length = MIN_MATCH - 1; /* length of best match */ + + /* Process the input block. */ + while (G1.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + INSERT_STRING(G1.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + G1.prev_length = match_length; + prev_match = G1.match_start; + match_length = MIN_MATCH - 1; + + if (hash_head != 0 && G1.prev_length < max_lazy_match + && G1.strstart - hash_head <= MAX_DIST + ) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + match_length = longest_match(hash_head); + /* longest_match() sets match_start */ + if (match_length > G1.lookahead) + match_length = G1.lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && G1.strstart - G1.match_start > TOO_FAR) { + /* If prev_match is also MIN_MATCH, G1.match_start is garbage + * but we will ignore the current match anyway. + */ + match_length--; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (G1.prev_length >= MIN_MATCH && match_length <= G1.prev_length) { + check_match(G1.strstart - 1, prev_match, G1.prev_length); + flush = ct_tally(G1.strstart - 1 - prev_match, G1.prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + G1.lookahead -= G1.prev_length - 1; + G1.prev_length -= 2; + do { + G1.strstart++; + INSERT_STRING(G1.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--G1.prev_length != 0); + match_available = 0; + match_length = MIN_MATCH - 1; + G1.strstart++; + if (flush) { + FLUSH_BLOCK(0); + G1.block_start = G1.strstart; + } + } else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr, "%c", G1.window[G1.strstart - 1])); + if (ct_tally(0, G1.window[G1.strstart - 1])) { + FLUSH_BLOCK(0); + G1.block_start = G1.strstart; + } + G1.strstart++; + G1.lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + G1.strstart++; + G1.lookahead--; + } + Assert(G1.strstart <= G1.isize && G1.lookahead <= G1.isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + fill_window_if_needed(); + } + if (match_available) + ct_tally(0, G1.window[G1.strstart - 1]); + + FLUSH_BLOCK(1); /* eof */ +} + +/* =========================================================================== + * Initialize the bit string routines. + */ +static void bi_init(void) +{ + //G1.bi_buf = 0; // globals are zeroed in pack_gzip() + //G1.bi_valid = 0; // globals are zeroed in pack_gzip() + //DEBUG_bits_sent(= 0L); // globals are zeroed in pack_gzip() +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new file + */ +static void lm_init(unsigned *flags16p) +{ + unsigned j; + + /* Initialize the hash table. */ + memset(head, 0, HASH_SIZE * sizeof(*head)); + /* prev will be initialized on the fly */ + + /* speed options for the general purpose bit flag */ + *flags16p |= 2; /* FAST 4, SLOW 2 */ + /* ??? reduce max_chain_length for binary files */ + + //G1.strstart = 0; // globals are zeroed in pack_gzip() + //G1.block_start = 0L; // globals are zeroed in pack_gzip() + + G1.lookahead = file_read(G1.window, + sizeof(int) <= 2 ? (unsigned) WSIZE : 2 * WSIZE); + + if (G1.lookahead == 0 || G1.lookahead == (unsigned) -1) { + G1.eofile = 1; + G1.lookahead = 0; + return; + } + //G1.eofile = 0; // globals are zeroed in pack_gzip() + + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + fill_window_if_needed(); + + //G1.ins_h = 0; // globals are zeroed in pack_gzip() + for (j = 0; j < MIN_MATCH - 1; j++) + UPDATE_HASH(G1.ins_h, G1.window[j]); + /* If lookahead < MIN_MATCH, ins_h is garbage, but this is + * not important since only literal bytes will be emitted. + */ +} + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + * One callsite in zip() + */ +static void ct_init(void) +{ + int n; /* iterates over tree elements */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + +// //G2.compressed_len = 0L; // globals are zeroed in pack_gzip() + +#ifdef NOT_NEEDED + if (G2.static_dtree[0].Len != 0) + return; /* ct_init already called */ +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES - 1; code++) { + G2.base_length[code] = length; + for (n = 0; n < (1 << extra_lbits[code]); n++) { + G2.length_code[length++] = code; + } + } + Assert(length == 256, "ct_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + G2.length_code[length - 1] = code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + G2.base_dist[code] = dist; + for (n = 0; n < (1 << extra_dbits[code]); n++) { + G2.dist_code[dist++] = code; + } + } + Assert(dist == 256, "ct_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for (; code < D_CODES; code++) { + G2.base_dist[code] = dist << 7; + for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { + G2.dist_code[256 + dist++] = code; + } + } + Assert(dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + //for (n = 0; n <= MAX_BITS; n++) // globals are zeroed in pack_gzip() + // G2.bl_count[n] = 0; + + n = 0; + while (n <= 143) { + G2.static_ltree[n++].Len = 8; + //G2.bl_count[8]++; + } + //G2.bl_count[8] = 143 + 1; + while (n <= 255) { + G2.static_ltree[n++].Len = 9; + //G2.bl_count[9]++; + } + //G2.bl_count[9] = 255 - 143; + while (n <= 279) { + G2.static_ltree[n++].Len = 7; + //G2.bl_count[7]++; + } + //G2.bl_count[7] = 279 - 255; + while (n <= 287) { + G2.static_ltree[n++].Len = 8; + //G2.bl_count[8]++; + } + //G2.bl_count[8] += 287 - 279; + G2.bl_count[7] = 279 - 255; + G2.bl_count[8] = (143 + 1) + (287 - 279); + G2.bl_count[9] = 255 - 143; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *) G2.static_ltree, L_CODES + 1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + G2.static_dtree[n].Len = 5; + G2.static_dtree[n].Code = bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(); +} + +/* =========================================================================== + * Deflate in to out. + * IN assertions: the input and output buffers are cleared. + */ +static void zip(void) +{ + unsigned deflate_flags; + + //G1.outcnt = 0; // globals are zeroed in pack_gzip() + + /* Write the header to the gzip file. See algorithm.doc for the format */ + /* magic header for gzip files: 1F 8B */ + /* compression method: 8 (DEFLATED) */ + /* general flags: 0 */ + put_32bit(0x00088b1f); + put_32bit(0); /* Unix timestamp */ + + /* Write deflated file to zip file */ + G1.crc = ~0; + + bi_init(); + ct_init(); + deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */ + lm_init(&deflate_flags); + + put_16bit(deflate_flags | 0x300); /* extra flags. OS id = 3 (Unix) */ + + /* The above 32-bit misaligns outbuf (10 bytes are stored), flush it */ + flush_outbuf_if_32bit_optimized(); + + deflate(); + + /* Write the crc and uncompressed size */ + put_32bit(~G1.crc); + put_32bit(G1.isize); + + flush_outbuf(); +} + +/* ======================================================================== */ +static +IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_state_t *xstate UNUSED_PARAM) +{ + /* Reinit G1.xxx except pointers to allocated buffers, and entire G2 */ + memset(&G1.crc, 0, (sizeof(G1) - offsetof(struct globals, crc)) + sizeof(G2)); + + /* Clear input and output buffers */ + //G1.outcnt = 0; +#ifdef DEBUG + //G1.insize = 0; +#endif + //G1.isize = 0; + + /* Reinit G2.xxx */ + G2.l_desc.dyn_tree = G2.dyn_ltree; + G2.l_desc.static_tree = G2.static_ltree; + G2.l_desc.extra_bits = extra_lbits; + G2.l_desc.extra_base = LITERALS + 1; + G2.l_desc.elems = L_CODES; + G2.l_desc.max_length = MAX_BITS; + //G2.l_desc.max_code = 0; + G2.d_desc.dyn_tree = G2.dyn_dtree; + G2.d_desc.static_tree = G2.static_dtree; + G2.d_desc.extra_bits = extra_dbits; + //G2.d_desc.extra_base = 0; + G2.d_desc.elems = D_CODES; + G2.d_desc.max_length = MAX_BITS; + //G2.d_desc.max_code = 0; + G2.bl_desc.dyn_tree = G2.bl_tree; + //G2.bl_desc.static_tree = NULL; + G2.bl_desc.extra_bits = extra_blbits, + //G2.bl_desc.extra_base = 0; + G2.bl_desc.elems = BL_CODES; + 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; +} + +#if ENABLE_FEATURE_GZIP_LONG_OPTIONS +static const char gzip_longopts[] ALIGN1 = + "stdout\0" No_argument "c" + "to-stdout\0" No_argument "c" + "force\0" No_argument "f" + "verbose\0" No_argument "v" +#if ENABLE_FEATURE_GZIP_DECOMPRESS + "decompress\0" No_argument "d" + "uncompress\0" No_argument "d" + "test\0" No_argument "t" +#endif + "quiet\0" No_argument "q" + "fast\0" No_argument "1" + "best\0" No_argument "9" + "no-name\0" No_argument "n" + ; +#endif + +/* + * Linux kernel build uses gzip -d -n. We accept and ignore -n. + * Man page says: + * -n --no-name + * gzip: do not save the original file name and time stamp. + * (The original name is always saved if the name had to be truncated.) + * gunzip: do not restore the original file name/time even if present + * (remove only the gzip suffix from the compressed file name). + * This option is the default when decompressing. + * -N --name + * gzip: always save the original file name and time stamp (this is the default) + * gunzip: restore the original file name and time stamp if present. + */ + +int gzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +#if ENABLE_FEATURE_GZIP_DECOMPRESS +int gzip_main(int argc, char **argv) +#else +int gzip_main(int argc UNUSED_PARAM, char **argv) +#endif +{ + unsigned opt; +#if 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)); + + /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ +#if ENABLE_FEATURE_GZIP_LONG_OPTIONS + opt = getopt32long(argv, BBUNPK_OPTSTR IF_FEATURE_GZIP_DECOMPRESS("dt") "n123456789", gzip_longopts); +#else + opt = getopt32(argv, BBUNPK_OPTSTR IF_FEATURE_GZIP_DECOMPRESS("dt") "n123456789"); +#endif +#if ENABLE_FEATURE_GZIP_DECOMPRESS /* gunzip_main may not be visible... */ + if (opt & (BBUNPK_OPT_DECOMPRESS|BBUNPK_OPT_TEST)) /* -d and/or -t */ + return gunzip_main(argc, argv); +#endif +#if ENABLE_FEATURE_GZIP_LEVELS + opt >>= (BBUNPK_OPTSTRLEN IF_FEATURE_GZIP_DECOMPRESS(+ 2) + 1); /* drop cfkvq[dt]n 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 &= BBUNPK_OPTSTRMASK; /* retain only -cfkvq */ + + /* Allocate all global buffers (for DYN_ALLOC option) */ + ALLOC(uch, G1.l_buf, INBUFSIZ); + ALLOC(uch, G1.outbuf, OUTBUFSIZ); + ALLOC(ush, G1.d_buf, DIST_BUFSIZE); + ALLOC(uch, G1.window, 2L * WSIZE); + ALLOC(ush, G1.prev, 1L << BITS); + + /* Initialize the CRC32 table */ + global_crc32_new_table_le(); + + argv += optind; + return bbunpack(argv, pack_gzip, append_ext, "gz"); +} diff --git a/busybox/archival/libarchive/Kbuild.src b/busybox/archival/libarchive/Kbuild.src new file mode 100644 index 00000000000000..e1a8a75291763d --- /dev/null +++ b/busybox/archival/libarchive/Kbuild.src @@ -0,0 +1,97 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 by Erik Andersen +# +# Licensed under GPLv2 or later, see file LICENSE in this source tree. + +lib-y:= common.o + +COMMON_FILES:= \ +\ + data_skip.o \ + data_extract_all.o \ + data_extract_to_stdout.o \ +\ + unsafe_symlink_target.o \ +\ + filter_accept_all.o \ + filter_accept_list.o \ + filter_accept_reject_list.o \ +\ + header_skip.o \ + header_list.o \ + header_verbose_list.o \ +\ + seek_by_read.o \ + seek_by_jump.o \ +\ + data_align.o \ + find_list_entry.o \ + init_handle.o + +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 + +lib-$(CONFIG_DPKG) += $(DPKG_FILES) +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 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 +# 'bzip2 -d', bunzip2 or bzcat selects FEATURE_BZIP2_DECOMPRESS +lib-$(CONFIG_FEATURE_BZIP2_DECOMPRESS) += open_transformer.o decompress_bunzip2.o +lib-$(CONFIG_FEATURE_UNZIP_BZIP2) += 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_FEATURE_UNZIP_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_FEATURE_UNZIP_XZ) += open_transformer.o decompress_unxz.o +# 'gzip -d', gunzip or zcat selects FEATURE_GZIP_DECOMPRESS +lib-$(CONFIG_FEATURE_GZIP_DECOMPRESS) += open_transformer.o decompress_gunzip.o +lib-$(CONFIG_UNCOMPRESS) += open_transformer.o decompress_uncompress.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 +lib-$(CONFIG_MAN) += open_transformer.o +lib-$(CONFIG_SETFONT) += open_transformer.o +lib-$(CONFIG_FEATURE_2_4_MODULES) += open_transformer.o +lib-$(CONFIG_MODINFO) += open_transformer.o +lib-$(CONFIG_INSMOD) += open_transformer.o +lib-$(CONFIG_DEPMOD) += open_transformer.o +lib-$(CONFIG_RMMOD) += open_transformer.o +lib-$(CONFIG_LSMOD) += open_transformer.o +lib-$(CONFIG_MODPROBE) += open_transformer.o +lib-$(CONFIG_MODPROBE_SMALL) += open_transformer.o + +lib-$(CONFIG_FEATURE_SEAMLESS_Z) += open_transformer.o decompress_uncompress.o +lib-$(CONFIG_FEATURE_SEAMLESS_GZ) += open_transformer.o decompress_gunzip.o +lib-$(CONFIG_FEATURE_SEAMLESS_BZ2) += open_transformer.o decompress_bunzip2.o +lib-$(CONFIG_FEATURE_SEAMLESS_LZMA) += open_transformer.o decompress_unlzma.o +lib-$(CONFIG_FEATURE_SEAMLESS_XZ) += open_transformer.o decompress_unxz.o +lib-$(CONFIG_FEATURE_COMPRESS_USAGE) += open_transformer.o decompress_bunzip2.o +lib-$(CONFIG_FEATURE_COMPRESS_BBCONFIG) += open_transformer.o decompress_bunzip2.o + +ifneq ($(lib-y),) +lib-y += $(COMMON_FILES) +endif diff --git a/busybox/archival/libarchive/bz/LICENSE b/busybox/archival/libarchive/bz/LICENSE new file mode 100644 index 00000000000000..da4346520f2153 --- /dev/null +++ b/busybox/archival/libarchive/bz/LICENSE @@ -0,0 +1,44 @@ +bzip2 applet in busybox is based on lightly-modified source +of bzip2 version 1.0.4. bzip2 source is distributed +under the following conditions (copied verbatim from LICENSE file) +=========================================================== + + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2006 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. 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 BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Julian Seward, Cambridge, UK. +jseward@bzip.org +bzip2/libbzip2 version 1.0.4 of 20 December 2006 diff --git a/busybox/archival/libarchive/bz/README b/busybox/archival/libarchive/bz/README new file mode 100644 index 00000000000000..fffd47b8a00f77 --- /dev/null +++ b/busybox/archival/libarchive/bz/README @@ -0,0 +1,90 @@ +This file is an abridged version of README from bzip2 1.0.4 +Build instructions (which are not relevant to busyboxed bzip2) +are removed. +=========================================================== + + +This is the README for bzip2/libzip2. +This version is fully compatible with the previous public releases. + +------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in this file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ + +Please read and be aware of the following: + + +WARNING: + + This program and library (attempts to) compress data by + performing several non-trivial transformations on it. + Unless you are 100% familiar with *all* the algorithms + contained herein, and with the consequences of modifying them, + you should NOT meddle with the compression or decompression + machinery. Incorrect changes can and very likely *will* + lead to disastrous loss of data. + + +DISCLAIMER: + + I TAKE NO RESPONSIBILITY FOR ANY LOSS OF DATA ARISING FROM THE + USE OF THIS PROGRAM/LIBRARY, HOWSOEVER CAUSED. + + Every compression of a file implies an assumption that the + compressed file can be decompressed to reproduce the original. + Great efforts in design, coding and testing have been made to + ensure that this program works correctly. However, the complexity + of the algorithms, and, in particular, the presence of various + special cases in the code which occur with very low but non-zero + probability make it impossible to rule out the possibility of bugs + remaining in the program. DO NOT COMPRESS ANY DATA WITH THIS + PROGRAM UNLESS YOU ARE PREPARED TO ACCEPT THE POSSIBILITY, HOWEVER + SMALL, THAT THE DATA WILL NOT BE RECOVERABLE. + + That is not to say this program is inherently unreliable. + Indeed, I very much hope the opposite is true. bzip2/libbzip2 + has been carefully constructed and extensively tested. + + +PATENTS: + + To the best of my knowledge, bzip2/libbzip2 does not use any + patented algorithms. However, I do not have the resources + to carry out a patent search. Therefore I cannot give any + guarantee of the above statement. + + +I hope you find bzip2 useful. Feel free to contact me at + jseward@bzip.org +if you have any suggestions or queries. Many people mailed me with +comments, suggestions and patches after the releases of bzip-0.15, +bzip-0.21, and bzip2 versions 0.1pl2, 0.9.0, 0.9.5, 1.0.0, 1.0.1, +1.0.2 and 1.0.3, and the changes in bzip2 are largely a result of this +feedback. I thank you for your comments. + +bzip2's "home" is http://www.bzip.org/ + +Julian Seward +jseward@bzip.org +Cambridge, UK. + +18 July 1996 (version 0.15) +25 August 1996 (version 0.21) + 7 August 1997 (bzip2, version 0.1) +29 August 1997 (bzip2, version 0.1pl2) +23 August 1998 (bzip2, version 0.9.0) + 8 June 1999 (bzip2, version 0.9.5) + 4 Sept 1999 (bzip2, version 0.9.5d) + 5 May 2000 (bzip2, version 1.0pre8) +30 December 2001 (bzip2, version 1.0.2pre1) +15 February 2005 (bzip2, version 1.0.3) +20 December 2006 (bzip2, version 1.0.4) diff --git a/busybox/archival/libarchive/bz/blocksort.c b/busybox/archival/libarchive/bz/blocksort.c new file mode 100644 index 00000000000000..92d6d825146f7d --- /dev/null +++ b/busybox/archival/libarchive/bz/blocksort.c @@ -0,0 +1,1079 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery ---*/ +/*--- blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +/* #include "bzlib_private.h" */ + +#define mswap(zz1, zz2) \ +{ \ + int32_t zztmp = zz1; \ + zz1 = zz2; \ + zz2 = zztmp; \ +} + +static +/* No measurable speed gain with inlining */ +/* ALWAYS_INLINE */ +void mvswap(uint32_t* ptr, int32_t zzp1, int32_t zzp2, int32_t zzn) +{ + while (zzn > 0) { + mswap(ptr[zzp1], ptr[zzp2]); + zzp1++; + zzp2++; + zzn--; + } +} + +static +ALWAYS_INLINE +int32_t mmin(int32_t a, int32_t b) +{ + return (a < b) ? a : b; +} + + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting ---*/ +/*--- algorithm, for repetitive blocks ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +inline +void fallbackSimpleSort(uint32_t* fmap, + uint32_t* eclass, + int32_t lo, + int32_t hi) +{ + int32_t i, j, tmp; + uint32_t ec_tmp; + + if (lo == hi) return; + + if (hi - lo > 3) { + for (i = hi-4; i >= lo; i--) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for (j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4) + fmap[j-4] = fmap[j]; + fmap[j-4] = tmp; + } + } + + for (i = hi-1; i >= lo; i--) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for (j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++) + fmap[j-1] = fmap[j]; + fmap[j-1] = tmp; + } +} + + +/*---------------------------------------------*/ +#define fpush(lz,hz) { \ + stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + sp++; \ +} + +#define fpop(lz,hz) { \ + sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ +} + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE 100 + +static +void fallbackQSort3(uint32_t* fmap, + uint32_t* eclass, + int32_t loSt, + int32_t hiSt) +{ + int32_t sp; + uint32_t r; + int32_t stackLo[FALLBACK_QSORT_STACK_SIZE]; + int32_t stackHi[FALLBACK_QSORT_STACK_SIZE]; + + r = 0; + + sp = 0; + fpush(loSt, hiSt); + + while (sp > 0) { + int32_t unLo, unHi, ltLo, gtHi, n, m; + int32_t lo, hi; + uint32_t med; + uint32_t r3; + + AssertH(sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004); + + fpop(lo, hi); + if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { + fallbackSimpleSort(fmap, eclass, lo, hi); + continue; + } + + /* Random partitioning. Median of 3 sometimes fails to + * avoid bad cases. Median of 9 seems to help but + * looks rather expensive. This too seems to work but + * is cheaper. Guidance for the magic constants + * 7621 and 32768 is taken from Sedgewick's algorithms + * book, chapter 35. + */ + r = ((r * 7621) + 1) % 32768; + r3 = r % 3; + if (r3 == 0) + med = eclass[fmap[lo]]; + else if (r3 == 1) + med = eclass[fmap[(lo+hi)>>1]]; + else + med = eclass[fmap[hi]]; + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) break; + n = (int32_t)eclass[fmap[unLo]] - (int32_t)med; + if (n == 0) { + mswap(fmap[unLo], fmap[ltLo]); + ltLo++; + unLo++; + continue; + } + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) break; + n = (int32_t)eclass[fmap[unHi]] - (int32_t)med; + if (n == 0) { + mswap(fmap[unHi], fmap[gtHi]); + gtHi--; unHi--; + continue; + } + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + mswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; + } + + AssertD(unHi == unLo-1, "fallbackQSort3(2)"); + + if (gtHi < ltLo) continue; + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(fmap, lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(fmap, unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + if (n - lo > hi - m) { + fpush(lo, n); + fpush(m, hi); + } else { + fpush(m, hi); + fpush(lo, n); + } + } +} + +#undef fpush +#undef fpop +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + * nblock > 0 + * eclass exists for [0 .. nblock-1] + * ((uint8_t*)eclass) [0 .. nblock-1] holds block + * ptr exists for [0 .. nblock-1] + * + * Post: + * ((uint8_t*)eclass) [0 .. nblock-1] holds block + * All other areas of eclass destroyed + * fmap [0 .. nblock-1] holds sorted order + * bhtab[0 .. 2+(nblock/32)] destroyed +*/ + +#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define WORD_BH(zz) bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz) ((zz) & 0x01f) + +static +void fallbackSort(EState* state) +{ + int32_t ftab[257]; + int32_t ftabCopy[256]; + int32_t H, i, j, k, l, r, cc, cc1; + int32_t nNotDone; + int32_t nBhtab; + /* params */ + uint32_t *const fmap = state->arr1; + uint32_t *const eclass = state->arr2; +#define eclass8 ((uint8_t*)eclass) + uint32_t *const bhtab = state->ftab; + const int32_t nblock = state->nblock; + + /* + * Initial 1-char radix sort to generate + * initial fmap and initial BH bits. + */ + for (i = 0; i < 257; i++) ftab[i] = 0; + for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; + for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; + + j = ftab[0]; /* bbox: optimized */ + for (i = 1; i < 257; i++) { + j += ftab[i]; + ftab[i] = j; + } + + for (i = 0; i < nblock; i++) { + j = eclass8[i]; + k = ftab[j] - 1; + ftab[j] = k; + fmap[k] = i; + } + + nBhtab = 2 + ((uint32_t)nblock / 32); /* bbox: unsigned div is easier */ + for (i = 0; i < nBhtab; i++) bhtab[i] = 0; + for (i = 0; i < 256; i++) SET_BH(ftab[i]); + + /* + * Inductively refine the buckets. Kind-of an + * "exponential radix sort" (!), inspired by the + * Manber-Myers suffix array construction algorithm. + */ + + /*-- set sentinel bits for block-end detection --*/ + for (i = 0; i < 32; i++) { + SET_BH(nblock + 2*i); + CLEAR_BH(nblock + 2*i + 1); + } + + /*-- the log(N) loop --*/ + H = 1; + while (1) { + j = 0; + for (i = 0; i < nblock; i++) { + if (ISSET_BH(i)) + j = i; + k = fmap[i] - H; + if (k < 0) + k += nblock; + eclass[k] = j; + } + + nNotDone = 0; + r = -1; + while (1) { + + /*-- find the next non-singleton bucket --*/ + k = r + 1; + while (ISSET_BH(k) && UNALIGNED_BH(k)) + k++; + if (ISSET_BH(k)) { + while (WORD_BH(k) == 0xffffffff) k += 32; + while (ISSET_BH(k)) k++; + } + l = k - 1; + if (l >= nblock) + break; + while (!ISSET_BH(k) && UNALIGNED_BH(k)) + k++; + if (!ISSET_BH(k)) { + while (WORD_BH(k) == 0x00000000) k += 32; + while (!ISSET_BH(k)) k++; + } + r = k - 1; + if (r >= nblock) + break; + + /*-- now [l, r] bracket current bucket --*/ + if (r > l) { + nNotDone += (r - l + 1); + fallbackQSort3(fmap, eclass, l, r); + + /*-- scan bucket and generate header bits-- */ + cc = -1; + for (i = l; i <= r; i++) { + cc1 = eclass[fmap[i]]; + if (cc != cc1) { + SET_BH(i); + cc = cc1; + } + } + } + } + + H *= 2; + if (H > nblock || nNotDone == 0) + break; + } + + /* + * Reconstruct the original block in + * eclass8 [0 .. nblock-1], since the + * previous phase destroyed it. + */ + j = 0; + for (i = 0; i < nblock; i++) { + while (ftabCopy[j] == 0) + j++; + ftabCopy[j]--; + eclass8[fmap[i]] = (uint8_t)j; + } + AssertH(j < 256, 1005); +#undef eclass8 +} + +#undef SET_BH +#undef CLEAR_BH +#undef ISSET_BH +#undef WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting ---*/ +/*--- algorithm. Faster for "normal" ---*/ +/*--- non-repetitive blocks. ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +NOINLINE +int mainGtU(EState* state, + uint32_t i1, + uint32_t i2) +{ + int32_t k; + uint8_t c1, c2; + uint16_t s1, s2; + + uint8_t *const block = state->block; + uint16_t *const quadrant = state->quadrant; + const int32_t nblock = state->nblock; + +/* Loop unrolling here is actually very useful + * (generated code is much simpler), + * code size increase is only 270 bytes (i386) + * but speeds up compression 10% overall + */ + +#if BZIP2_SPEED >= 1 + +#define TIMES_8(code) \ + code; code; code; code; \ + code; code; code; code; +#define TIMES_12(code) \ + code; code; code; code; \ + code; code; code; code; \ + code; code; code; code; + +#else + +#define TIMES_8(code) \ +{ \ + int nn = 8; \ + do { \ + code; \ + } while (--nn); \ +} +#define TIMES_12(code) \ +{ \ + int nn = 12; \ + do { \ + code; \ + } while (--nn); \ +} + +#endif + + AssertD(i1 != i2, "mainGtU"); + TIMES_12( + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + ) + + k = nblock + 8; + + do { + TIMES_8( + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + ) + + if (i1 >= nblock) i1 -= nblock; + if (i2 >= nblock) i2 -= nblock; + + state->budget--; + k -= 8; + } while (k >= 0); + + return False; +} +#undef TIMES_8 +#undef TIMES_12 + +/*---------------------------------------------*/ +/* + * Knuth's increments seem to work better + * than Incerpi-Sedgewick here. Possibly + * because the number of elems to sort is + * usually small, typically <= 20. + */ +static +const uint32_t incs[14] = { + 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 +}; + +static +void mainSimpleSort(EState* state, + int32_t lo, + int32_t hi, + int32_t d) +{ + uint32_t *const ptr = state->ptr; + + /* At which increment to start? */ + int hp = 0; + { + int bigN = hi - lo; + if (bigN <= 0) + return; + while (incs[hp] <= bigN) + hp++; + hp--; + } + + for (; hp >= 0; hp--) { + int32_t i; + unsigned h; + + h = incs[hp]; + i = lo + h; + while (1) { + unsigned j; + unsigned v; + + if (i > hi) break; + v = ptr[i]; + j = i; + while (mainGtU(state, ptr[j-h]+d, v+d)) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + +/* 1.5% overall speedup, +290 bytes */ +#if BZIP2_SPEED >= 3 + /*-- copy 2 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while (mainGtU(state, ptr[j-h]+d, v+d)) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + /*-- copy 3 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while (mainGtU(state, ptr[j-h]+d, v+d)) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; +#endif + if (state->budget < 0) return; + } + } +} + + +/*---------------------------------------------*/ +/* + * The following is an implementation of + * an elegant 3-way quicksort for strings, + * described in a paper "Fast Algorithms for + * Sorting and Searching Strings", by Robert + * Sedgewick and Jon L. Bentley. + */ + +static +ALWAYS_INLINE +uint8_t mmed3(uint8_t a, uint8_t b, uint8_t c) +{ + uint8_t t; + if (a > b) { + t = a; + a = b; + b = t; + } + /* here b >= a */ + if (b > c) { + b = c; + if (a > b) + b = a; + } + return b; +} + +#define mpush(lz,hz,dz) \ +{ \ + stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + stackD [sp] = dz; \ + sp++; \ +} + +#define mpop(lz,hz,dz) \ +{ \ + sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ + dz = stackD [sp]; \ +} + +#define mnextsize(az) (nextHi[az] - nextLo[az]) + +#define mnextswap(az,bz) \ +{ \ + int32_t tz; \ + tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ + tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ + tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; \ +} + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static NOINLINE +void mainQSort3(EState* state, + int32_t loSt, + int32_t hiSt + /*int32_t dSt*/) +{ + enum { dSt = BZ_N_RADIX }; + int32_t unLo, unHi, ltLo, gtHi, n, m, med; + int32_t sp, lo, hi, d; + + int32_t stackLo[MAIN_QSORT_STACK_SIZE]; + int32_t stackHi[MAIN_QSORT_STACK_SIZE]; + int32_t stackD [MAIN_QSORT_STACK_SIZE]; + + int32_t nextLo[3]; + int32_t nextHi[3]; + int32_t nextD [3]; + + uint32_t *const ptr = state->ptr; + uint8_t *const block = state->block; + + sp = 0; + mpush(loSt, hiSt, dSt); + + while (sp > 0) { + AssertH(sp < MAIN_QSORT_STACK_SIZE - 2, 1001); + + mpop(lo, hi, d); + if (hi - lo < MAIN_QSORT_SMALL_THRESH + || d > MAIN_QSORT_DEPTH_THRESH + ) { + mainSimpleSort(state, lo, hi, d); + if (state->budget < 0) + return; + continue; + } + med = (int32_t) mmed3(block[ptr[lo ] + d], + block[ptr[hi ] + d], + block[ptr[(lo+hi) >> 1] + d]); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) + break; + n = ((int32_t)block[ptr[unLo]+d]) - med; + if (n == 0) { + mswap(ptr[unLo], ptr[ltLo]); + ltLo++; + unLo++; + continue; + } + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) + break; + n = ((int32_t)block[ptr[unHi]+d]) - med; + if (n == 0) { + mswap(ptr[unHi], ptr[gtHi]); + gtHi--; + unHi--; + continue; + } + if (n < 0) break; + unHi--; + } + if (unLo > unHi) + break; + mswap(ptr[unLo], ptr[unHi]); + unLo++; + unHi--; + } + + AssertD(unHi == unLo-1, "mainQSort3(2)"); + + if (gtHi < ltLo) { + mpush(lo, hi, d + 1); + continue; + } + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(ptr, lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(ptr, unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; + nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; + nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + + if (mnextsize(0) < mnextsize(1)) mnextswap(0, 1); + if (mnextsize(1) < mnextsize(2)) mnextswap(1, 2); + if (mnextsize(0) < mnextsize(1)) mnextswap(0, 1); + + AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)"); + AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)"); + + mpush(nextLo[0], nextHi[0], nextD[0]); + mpush(nextLo[1], nextHi[1], nextD[1]); + mpush(nextLo[2], nextHi[2], nextD[2]); + } +} + +#undef mpush +#undef mpop +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + * nblock > N_OVERSHOOT + * block32 exists for [0 .. nblock-1 +N_OVERSHOOT] + * ((uint8_t*)block32) [0 .. nblock-1] holds block + * ptr exists for [0 .. nblock-1] + * + * Post: + * ((uint8_t*)block32) [0 .. nblock-1] holds block + * All other areas of block32 destroyed + * ftab[0 .. 65536] destroyed + * ptr [0 .. nblock-1] holds sorted order + * if (*budget < 0), sorting was abandoned + */ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static NOINLINE +void mainSort(EState* state) +{ + int32_t i, j; + Bool bigDone[256]; + uint8_t runningOrder[256]; + /* bbox: moved to EState to save stack + int32_t copyStart[256]; + int32_t copyEnd [256]; + */ +#define copyStart (state->mainSort__copyStart) +#define copyEnd (state->mainSort__copyEnd) + + uint32_t *const ptr = state->ptr; + uint8_t *const block = state->block; + uint32_t *const ftab = state->ftab; + const int32_t nblock = state->nblock; + uint16_t *const quadrant = state->quadrant; + + /*-- set up the 2-byte frequency table --*/ + /* was: for (i = 65536; i >= 0; i--) ftab[i] = 0; */ + memset(ftab, 0, 65537 * sizeof(ftab[0])); + + j = block[0] << 8; + i = nblock - 1; +/* 3%, +300 bytes */ +#if BZIP2_SPEED >= 2 + for (; i >= 3; i -= 4) { + quadrant[i] = 0; + j = (j >> 8) | (((unsigned)block[i]) << 8); + ftab[j]++; + quadrant[i-1] = 0; + j = (j >> 8) | (((unsigned)block[i-1]) << 8); + ftab[j]++; + quadrant[i-2] = 0; + j = (j >> 8) | (((unsigned)block[i-2]) << 8); + ftab[j]++; + quadrant[i-3] = 0; + j = (j >> 8) | (((unsigned)block[i-3]) << 8); + ftab[j]++; + } +#endif + for (; i >= 0; i--) { + quadrant[i] = 0; + j = (j >> 8) | (((unsigned)block[i]) << 8); + ftab[j]++; + } + + /*-- (emphasises close relationship of block & quadrant) --*/ + for (i = 0; i < BZ_N_OVERSHOOT; i++) { + block [nblock+i] = block[i]; + quadrant[nblock+i] = 0; + } + + /*-- Complete the initial radix sort --*/ + j = ftab[0]; /* bbox: optimized */ + for (i = 1; i <= 65536; i++) { + j += ftab[i]; + ftab[i] = j; + } + + { + unsigned s; + s = block[0] << 8; + i = nblock - 1; +#if BZIP2_SPEED >= 2 + for (; i >= 3; i -= 4) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] - 1; + ftab[s] = j; + ptr[j] = i; + s = (s >> 8) | (block[i-1] << 8); + j = ftab[s] - 1; + ftab[s] = j; + ptr[j] = i-1; + s = (s >> 8) | (block[i-2] << 8); + j = ftab[s] - 1; + ftab[s] = j; + ptr[j] = i-2; + s = (s >> 8) | (block[i-3] << 8); + j = ftab[s] - 1; + ftab[s] = j; + ptr[j] = i-3; + } +#endif + for (; i >= 0; i--) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] - 1; + ftab[s] = j; + ptr[j] = i; + } + } + + /* + * Now ftab contains the first loc of every small bucket. + * Calculate the running order, from smallest to largest + * big bucket. + */ + for (i = 0; i <= 255; i++) { + bigDone [i] = False; + runningOrder[i] = i; + } + + { + /* bbox: was: int32_t h = 1; */ + /* do h = 3 * h + 1; while (h <= 256); */ + unsigned h = 364; + + do { + /*h = h / 3;*/ + h = (h * 171) >> 9; /* bbox: fast h/3 */ + for (i = h; i <= 255; i++) { + unsigned vv, jh; + vv = runningOrder[i]; /* uint8[] */ + j = i; + while (jh = j - h, BIGFREQ(runningOrder[jh]) > BIGFREQ(vv)) { + runningOrder[j] = runningOrder[jh]; + j = jh; + if (j < h) + break; + } + runningOrder[j] = vv; + } + } while (h != 1); + } + + /* + * The main sorting loop. + */ + + for (i = 0; /*i <= 255*/; i++) { + unsigned ss; + + /* + * Process big buckets, starting with the least full. + * Basically this is a 3-step process in which we call + * mainQSort3 to sort the small buckets [ss, j], but + * also make a big effort to avoid the calls if we can. + */ + ss = runningOrder[i]; + + /* + * Step 1: + * Complete the big bucket [ss] by quicksorting + * any unsorted small buckets [ss, j], for j != ss. + * Hopefully previous pointer-scanning phases have already + * completed many of the small buckets [ss, j], so + * we don't have to sort them at all. + */ + for (j = 0; j <= 255; j++) { + if (j != ss) { + unsigned sb; + sb = (ss << 8) + j; + if (!(ftab[sb] & SETMASK)) { + int32_t lo = ftab[sb] /*& CLEARMASK (redundant)*/; + int32_t hi = (ftab[sb+1] & CLEARMASK) - 1; + if (hi > lo) { + mainQSort3(state, lo, hi /*,BZ_N_RADIX*/); + if (state->budget < 0) return; + } + } + ftab[sb] |= SETMASK; + } + } + + AssertH(!bigDone[ss], 1006); + + /* + * Step 2: + * Now scan this big bucket [ss] so as to synthesise the + * sorted order for small buckets [t, ss] for all t, + * including, magically, the bucket [ss,ss] too. + * This will avoid doing Real Work in subsequent Step 1's. + */ + { + for (j = 0; j <= 255; j++) { + copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; + copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; + } + for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { + unsigned c1; + int32_t k; + k = ptr[j] - 1; + if (k < 0) + k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[copyStart[c1]++] = k; + } + for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { + unsigned c1; + int32_t k; + k = ptr[j]-1; + if (k < 0) + k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[copyEnd[c1]--] = k; + } + } + + /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. + * Necessity for this case is demonstrated by compressing + * a sequence of approximately 48.5 million of character + * 251; 1.0.0/1.0.1 will then die here. */ + AssertH((copyStart[ss]-1 == copyEnd[ss]) \ + || (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), 1007); + + for (j = 0; j <= 255; j++) + ftab[(j << 8) + ss] |= SETMASK; + + if (i == 255) + break; + + /* + * Step 3: + * The [ss] big bucket is now done. Record this fact, + * and update the quadrant descriptors. Remember to + * update quadrants in the overshoot area too, if + * necessary. The "if (i < 255)" test merely skips + * this updating for the last bucket processed, since + * updating for the last bucket is pointless. + * + * The quadrant array provides a way to incrementally + * cache sort orderings, as they appear, so as to + * make subsequent comparisons in fullGtU() complete + * faster. For repetitive blocks this makes a big + * difference (but not big enough to be able to avoid + * the fallback sorting mechanism, exponential radix sort). + * + * The precise meaning is: at all times: + * + * for 0 <= i < nblock and 0 <= j <= nblock + * + * if block[i] != block[j], + * + * then the relative values of quadrant[i] and + * quadrant[j] are meaningless. + * + * else { + * if quadrant[i] < quadrant[j] + * then the string starting at i lexicographically + * precedes the string starting at j + * + * else if quadrant[i] > quadrant[j] + * then the string starting at j lexicographically + * precedes the string starting at i + * + * else + * the relative ordering of the strings starting + * at i and j has not yet been determined. + * } + */ + bigDone[ss] = True; + + { + unsigned bbStart = ftab[ss << 8] & CLEARMASK; + unsigned bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; + unsigned shifts = 0; + + while ((bbSize >> shifts) > 65534) shifts++; + + for (j = bbSize-1; j >= 0; j--) { + unsigned a2update = ptr[bbStart + j]; /* uint32[] */ + uint16_t qVal = (uint16_t)(j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZ_N_OVERSHOOT) + quadrant[a2update + nblock] = qVal; + } + AssertH(((bbSize-1) >> shifts) <= 65535, 1002); + } + } +#undef runningOrder +#undef copyStart +#undef copyEnd +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: + * nblock > 0 + * arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] + * ((uint8_t*)arr2)[0 .. nblock-1] holds block + * arr1 exists for [0 .. nblock-1] + * + * Post: + * ((uint8_t*)arr2) [0 .. nblock-1] holds block + * All other areas of block destroyed + * ftab[0 .. 65536] destroyed + * arr1[0 .. nblock-1] holds sorted order + */ +static NOINLINE +int32_t BZ2_blockSort(EState* state) +{ + /* In original bzip2 1.0.4, it's a parameter, but 30 + * (which was the default) should work ok. */ + enum { wfact = 30 }; + unsigned i; + int32_t origPtr = origPtr; + + if (state->nblock >= 10000) { + /* Calculate the location for quadrant, remembering to get + * the alignment right. Assumes that &(block[0]) is at least + * 2-byte aligned -- this should be ok since block is really + * the first section of arr2. + */ + i = state->nblock + BZ_N_OVERSHOOT; + if (i & 1) + i++; + state->quadrant = (uint16_t*) &(state->block[i]); + + /* (wfact-1) / 3 puts the default-factor-30 + * transition point at very roughly the same place as + * with v0.1 and v0.9.0. + * Not that it particularly matters any more, since the + * resulting compressed stream is now the same regardless + * of whether or not we use the main sort or fallback sort. + */ + state->budget = state->nblock * ((wfact-1) / 3); + mainSort(state); + if (state->budget >= 0) + goto good; + } + fallbackSort(state); + good: + +#if BZ_LIGHT_DEBUG + origPtr = -1; +#endif + for (i = 0; i < state->nblock; i++) { + if (state->ptr[i] == 0) { + origPtr = i; + break; + } + } + + AssertH(origPtr != -1, 1003); + return origPtr; +} + + +/*-------------------------------------------------------------*/ +/*--- end blocksort.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/busybox/archival/libarchive/bz/bzlib.c b/busybox/archival/libarchive/bz/bzlib.c new file mode 100644 index 00000000000000..9af2f026db3cde --- /dev/null +++ b/busybox/archival/libarchive/bz/bzlib.c @@ -0,0 +1,429 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Library top-level functions. ---*/ +/*--- bzlib.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +/* CHANGES + * 0.9.0 -- original version. + * 0.9.0a/b -- no changes in this file. + * 0.9.0c -- made zero-length BZ_FLUSH work correctly in bzCompress(). + * fixed bzWrite/bzRead to ignore zero-length requests. + * fixed bzread to correctly handle read requests after EOF. + * wrong parameter order in call to bzDecompressInit in + * bzBuffToBuffDecompress. Fixed. + */ + +/* #include "bzlib_private.h" */ + +/*---------------------------------------------------*/ +/*--- Compression stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +#if BZ_LIGHT_DEBUG +static +void bz_assert_fail(int errcode) +{ + /* if (errcode == 1007) bb_error_msg_and_die("probably bad RAM"); */ + bb_error_msg_and_die("internal error %d", errcode); +} +#endif + +/*---------------------------------------------------*/ +static +void prepare_new_block(EState* s) +{ + int i; + s->nblock = 0; + //indexes into s->zbits[], initialzation moved to init of s->zbits + //s->posZ = s->zbits; // was: s->numZ = 0; + //s->state_out_pos = s->zbits; + BZ_INITIALISE_CRC(s->blockCRC); + /* inlined memset would be nice to have here */ + for (i = 0; i < 256; i++) + s->inUse[i] = 0; + s->blockNo++; +} + + +/*---------------------------------------------------*/ +static +ALWAYS_INLINE +void init_RL(EState* s) +{ + s->state_in_ch = 256; + s->state_in_len = 0; +} + + +static +int isempty_RL(EState* s) +{ + return (s->state_in_ch >= 256 || s->state_in_len <= 0); +} + + +/*---------------------------------------------------*/ +static +void BZ2_bzCompressInit(bz_stream *strm, int blockSize100k) +{ + unsigned n; + EState* s; + + s = xzalloc(sizeof(EState)); + s->strm = strm; + + n = 100000 * blockSize100k; + s->arr1 = xmalloc(n * sizeof(uint32_t)); + s->mtfv = (uint16_t*)s->arr1; + s->ptr = (uint32_t*)s->arr1; + s->arr2 = xmalloc((n + BZ_N_OVERSHOOT) * sizeof(uint32_t)); + s->block = (uint8_t*)s->arr2; + s->ftab = xmalloc(65537 * sizeof(uint32_t)); + + s->crc32table = crc32_filltable(NULL, 1); + + s->state = BZ_S_INPUT; + s->mode = BZ_M_RUNNING; + s->blockSize100k = blockSize100k; + s->nblockMAX = n - 19; + + strm->state = s; + /*strm->total_in = 0;*/ + strm->total_out = 0; + init_RL(s); + prepare_new_block(s); +} + + +/*---------------------------------------------------*/ +static +void add_pair_to_block(EState* s) +{ + int32_t i; + uint8_t ch = (uint8_t)(s->state_in_ch); + for (i = 0; i < s->state_in_len; i++) { + BZ_UPDATE_CRC(s, s->blockCRC, ch); + } + s->inUse[s->state_in_ch] = 1; + switch (s->state_in_len) { + case 3: + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + /* fall through */ + case 2: + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + /* fall through */ + case 1: + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + break; + default: + s->inUse[s->state_in_len - 4] = 1; + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + s->block[s->nblock] = (uint8_t)ch; s->nblock++; + s->block[s->nblock] = (uint8_t)(s->state_in_len - 4); + s->nblock++; + break; + } +} + + +/*---------------------------------------------------*/ +static +void flush_RL(EState* s) +{ + if (s->state_in_ch < 256) add_pair_to_block(s); + init_RL(s); +} + + +/*---------------------------------------------------*/ +#define ADD_CHAR_TO_BLOCK(zs, zchh0) \ +{ \ + uint32_t zchh = (uint32_t)(zchh0); \ + /*-- fast track the common case --*/ \ + if (zchh != zs->state_in_ch && zs->state_in_len == 1) { \ + uint8_t ch = (uint8_t)(zs->state_in_ch); \ + BZ_UPDATE_CRC(zs, zs->blockCRC, ch); \ + zs->inUse[zs->state_in_ch] = 1; \ + zs->block[zs->nblock] = (uint8_t)ch; \ + zs->nblock++; \ + zs->state_in_ch = zchh; \ + } \ + else \ + /*-- general, uncommon cases --*/ \ + if (zchh != zs->state_in_ch || zs->state_in_len == 255) { \ + if (zs->state_in_ch < 256) \ + add_pair_to_block(zs); \ + zs->state_in_ch = zchh; \ + zs->state_in_len = 1; \ + } else { \ + zs->state_in_len++; \ + } \ +} + + +/*---------------------------------------------------*/ +static +void /*Bool*/ copy_input_until_stop(EState* s) +{ + /*Bool progress_in = False;*/ + +#ifdef SAME_CODE_AS_BELOW + if (s->mode == BZ_M_RUNNING) { + /*-- fast track the common case --*/ + while (1) { + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*progress_in = True;*/ + ADD_CHAR_TO_BLOCK(s, (uint32_t)(*(uint8_t*)(s->strm->next_in))); + s->strm->next_in++; + s->strm->avail_in--; + /*s->strm->total_in++;*/ + } + } else +#endif + { + /*-- general, uncommon case --*/ + while (1) { + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + //# /*-- flush/finish end? --*/ + //# if (s->avail_in_expect == 0) break; + /*progress_in = True;*/ + ADD_CHAR_TO_BLOCK(s, *(uint8_t*)(s->strm->next_in)); + s->strm->next_in++; + s->strm->avail_in--; + /*s->strm->total_in++;*/ + //# s->avail_in_expect--; + } + } + /*return progress_in;*/ +} + + +/*---------------------------------------------------*/ +static +void /*Bool*/ copy_output_until_stop(EState* s) +{ + /*Bool progress_out = False;*/ + + while (1) { + /*-- no output space? --*/ + if (s->strm->avail_out == 0) break; + + /*-- block done? --*/ + if (s->state_out_pos >= s->posZ) break; + + /*progress_out = True;*/ + *(s->strm->next_out) = *s->state_out_pos++; + s->strm->avail_out--; + s->strm->next_out++; + s->strm->total_out++; + } + /*return progress_out;*/ +} + + +/*---------------------------------------------------*/ +static +void /*Bool*/ handle_compress(bz_stream *strm) +{ + /*Bool progress_in = False;*/ + /*Bool progress_out = False;*/ + EState* s = strm->state; + + while (1) { + if (s->state == BZ_S_OUTPUT) { + /*progress_out |=*/ copy_output_until_stop(s); + if (s->state_out_pos < s->posZ) break; + if (s->mode == BZ_M_FINISHING + //# && s->avail_in_expect == 0 + && s->strm->avail_in == 0 + && isempty_RL(s)) + break; + prepare_new_block(s); + s->state = BZ_S_INPUT; +#ifdef FLUSH_IS_UNUSED + if (s->mode == BZ_M_FLUSHING + && s->avail_in_expect == 0 + && isempty_RL(s)) + break; +#endif + } + + if (s->state == BZ_S_INPUT) { + /*progress_in |=*/ copy_input_until_stop(s); + //#if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { + if (s->mode != BZ_M_RUNNING && s->strm->avail_in == 0) { + flush_RL(s); + BZ2_compressBlock(s, (s->mode == BZ_M_FINISHING)); + s->state = BZ_S_OUTPUT; + } else + if (s->nblock >= s->nblockMAX) { + BZ2_compressBlock(s, 0); + s->state = BZ_S_OUTPUT; + } else + if (s->strm->avail_in == 0) { + break; + } + } + } + + /*return progress_in || progress_out;*/ +} + + +/*---------------------------------------------------*/ +static +int BZ2_bzCompress(bz_stream *strm, int action) +{ + /*Bool progress;*/ + EState* s; + + s = strm->state; + + switch (s->mode) { + case BZ_M_RUNNING: + if (action == BZ_RUN) { + /*progress =*/ handle_compress(strm); + /*return progress ? BZ_RUN_OK : BZ_PARAM_ERROR;*/ + return BZ_RUN_OK; + } +#ifdef FLUSH_IS_UNUSED + else + if (action == BZ_FLUSH) { + //#s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FLUSHING; + goto case_BZ_M_FLUSHING; + } +#endif + else + /*if (action == BZ_FINISH)*/ { + //#s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FINISHING; + goto case_BZ_M_FINISHING; + } + +#ifdef FLUSH_IS_UNUSED + case_BZ_M_FLUSHING: + case BZ_M_FLUSHING: + /*if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR;*/ + /*progress =*/ handle_compress(strm); + if (s->avail_in_expect > 0 || !isempty_RL(s) || s->state_out_pos < s->posZ) + return BZ_FLUSH_OK; + s->mode = BZ_M_RUNNING; + return BZ_RUN_OK; +#endif + + case_BZ_M_FINISHING: + /*case BZ_M_FINISHING:*/ + default: + /*if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR;*/ + /*progress =*/ handle_compress(strm); + /*if (!progress) return BZ_SEQUENCE_ERROR;*/ + //#if (s->avail_in_expect > 0 || !isempty_RL(s) || s->state_out_pos < s->posZ) + //# return BZ_FINISH_OK; + if (s->strm->avail_in > 0 || !isempty_RL(s) || s->state_out_pos < s->posZ) + return BZ_FINISH_OK; + /*s->mode = BZ_M_IDLE;*/ + return BZ_STREAM_END; + } + /* return BZ_OK; --not reached--*/ +} + + +/*---------------------------------------------------*/ +static +void BZ2_bzCompressEnd(bz_stream *strm) +{ + EState* s; + + s = strm->state; + free(s->arr1); + free(s->arr2); + free(s->ftab); + free(s->crc32table); + free(s); +} + + +/*---------------------------------------------------*/ +/*--- Misc convenience stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +#ifdef EXAMPLE_CODE_FOR_MEM_TO_MEM_COMPRESSION +static +int BZ2_bzBuffToBuffCompress(char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL + || source == NULL + || blockSize100k < 1 || blockSize100k > 9 + ) { + return BZ_PARAM_ERROR; + } + + BZ2_bzCompressInit(&strm, blockSize100k); + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzCompress(&strm, BZ_FINISH); + if (ret == BZ_FINISH_OK) goto output_overflow; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzCompressEnd(&strm); + return BZ_OK; + + output_overflow: + BZ2_bzCompressEnd(&strm); + return BZ_OUTBUFF_FULL; + + errhandler: + BZ2_bzCompressEnd(&strm); + return ret; +} +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/busybox/archival/libarchive/bz/bzlib.h b/busybox/archival/libarchive/bz/bzlib.h new file mode 100644 index 00000000000000..1bb811c4a18a93 --- /dev/null +++ b/busybox/archival/libarchive/bz/bzlib.h @@ -0,0 +1,65 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Public header file for the library. ---*/ +/*--- bzlib.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +#define BZ_RUN 0 +#define BZ_FLUSH 1 +#define BZ_FINISH 2 + +#define BZ_OK 0 +#define BZ_RUN_OK 1 +#define BZ_FLUSH_OK 2 +#define BZ_FINISH_OK 3 +#define BZ_STREAM_END 4 +#define BZ_SEQUENCE_ERROR (-1) +#define BZ_PARAM_ERROR (-2) +#define BZ_MEM_ERROR (-3) +#define BZ_DATA_ERROR (-4) +#define BZ_DATA_ERROR_MAGIC (-5) +#define BZ_IO_ERROR (-6) +#define BZ_UNEXPECTED_EOF (-7) +#define BZ_OUTBUFF_FULL (-8) +#define BZ_CONFIG_ERROR (-9) + +typedef struct bz_stream { + void *state; + char *next_in; + char *next_out; + unsigned avail_in; + unsigned avail_out; + /*unsigned long long total_in;*/ + unsigned long long total_out; +} bz_stream; + +/*-- Core (low-level) library functions --*/ + +static void BZ2_bzCompressInit(bz_stream *strm, int blockSize100k); +static int BZ2_bzCompress(bz_stream *strm, int action); +#if ENABLE_FEATURE_CLEAN_UP +static void BZ2_bzCompressEnd(bz_stream *strm); +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/busybox/archival/libarchive/bz/bzlib_private.h b/busybox/archival/libarchive/bz/bzlib_private.h new file mode 100644 index 00000000000000..ea0f29b7cf9f1f --- /dev/null +++ b/busybox/archival/libarchive/bz/bzlib_private.h @@ -0,0 +1,223 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Private header file for the library. ---*/ +/*--- bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +/* #include "bzlib.h" */ + +/*-- General stuff. --*/ + +typedef unsigned char Bool; + +#define True ((Bool)1) +#define False ((Bool)0) + +#if BZ_LIGHT_DEBUG +static void bz_assert_fail(int errcode) NORETURN; +#define AssertH(cond, errcode) \ +do { \ + if (!(cond)) \ + bz_assert_fail(errcode); \ +} while (0) +#else +#define AssertH(cond, msg) do { } while (0) +#endif + +#if BZ_DEBUG +#define AssertD(cond, msg) \ +do { \ + if (!(cond)) \ + bb_error_msg_and_die("(debug build): internal error %s", msg); \ +} while (0) +#else +#define AssertD(cond, msg) do { } while (0) +#endif + + +/*-- Header bytes. --*/ + +#define BZ_HDR_B 0x42 /* 'B' */ +#define BZ_HDR_Z 0x5a /* 'Z' */ +#define BZ_HDR_h 0x68 /* 'h' */ +#define BZ_HDR_0 0x30 /* '0' */ + +#define BZ_HDR_BZh0 0x425a6830 + +/*-- Constants for the back end. --*/ + +#define BZ_MAX_ALPHA_SIZE 258 +#define BZ_MAX_CODE_LEN 23 + +#define BZ_RUNA 0 +#define BZ_RUNB 1 + +#define BZ_N_GROUPS 6 +#define BZ_G_SIZE 50 +#define BZ_N_ITERS 4 + +#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE)) + + +/*-- Stuff for doing CRCs. --*/ + +#define BZ_INITIALISE_CRC(crcVar) \ +{ \ + crcVar = 0xffffffffL; \ +} + +#define BZ_FINALISE_CRC(crcVar) \ +{ \ + crcVar = ~(crcVar); \ +} + +#define BZ_UPDATE_CRC(s, crcVar, cha) \ +{ \ + crcVar = (crcVar << 8) ^ s->crc32table[(crcVar >> 24) ^ ((uint8_t)cha)]; \ +} + + +/*-- States and modes for compression. --*/ + +#define BZ_M_IDLE 1 +#define BZ_M_RUNNING 2 +#define BZ_M_FLUSHING 3 +#define BZ_M_FINISHING 4 + +#define BZ_S_OUTPUT 1 +#define BZ_S_INPUT 2 + +#define BZ_N_RADIX 2 +#define BZ_N_QSORT 12 +#define BZ_N_SHELL 18 +#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) + + +/*-- Structure holding all the compression-side stuff. --*/ + +typedef struct EState { + /* pointer back to the struct bz_stream */ + bz_stream *strm; + + /* mode this stream is in, and whether inputting */ + /* or outputting data */ + uint8_t mode; + uint8_t state; + + /* misc administratium */ + uint8_t blockSize100k; + + /* remembers avail_in when flush/finish requested */ +/* bbox: not needed, strm->avail_in always has the same value */ +/* commented out with '//#' throughout the code */ + /* uint32_t avail_in_expect; */ + + /* for doing the block sorting */ + uint32_t *arr1; + uint32_t *arr2; + uint32_t *ftab; + + uint16_t *quadrant; + int32_t budget; + + /* aliases for arr1 and arr2 */ + uint32_t *ptr; + uint8_t *block; + uint16_t *mtfv; + uint8_t *zbits; + + /* run-length-encoding of the input */ + uint32_t state_in_ch; + int32_t state_in_len; + + /* input and output limits and current posns */ + int32_t nblock; + int32_t nblockMAX; + //int32_t numZ; // index into s->zbits[], replaced by pointer: + uint8_t *posZ; + uint8_t *state_out_pos; + + /* the buffer for bit stream creation */ + uint32_t bsBuff; + int32_t bsLive; + + /* guess what */ + uint32_t *crc32table; + + /* block and combined CRCs */ + uint32_t blockCRC; + uint32_t combinedCRC; + + /* misc administratium */ + int32_t blockNo; + + /* stuff for coding the MTF values */ + int32_t nMTF; + + /* map of bytes used in block */ + int32_t nInUse; + Bool inUse[256] ALIGNED(sizeof(long)); + uint8_t unseqToSeq[256]; + + /* stuff for coding the MTF values */ + int32_t mtfFreq [BZ_MAX_ALPHA_SIZE]; + uint8_t selector [BZ_MAX_SELECTORS]; + uint8_t selectorMtf[BZ_MAX_SELECTORS]; + + uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + + /* stack-saving measures: these can be local, but they are too big */ + int32_t sendMTFValues__code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + int32_t sendMTFValues__rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +#if BZIP2_SPEED >= 5 + /* second dimension: only 3 needed; 4 makes index calculations faster */ + uint32_t sendMTFValues__len_pack[BZ_MAX_ALPHA_SIZE][4]; +#endif + int32_t BZ2_hbMakeCodeLengths__heap [BZ_MAX_ALPHA_SIZE + 2]; + int32_t BZ2_hbMakeCodeLengths__weight[BZ_MAX_ALPHA_SIZE * 2]; + int32_t BZ2_hbMakeCodeLengths__parent[BZ_MAX_ALPHA_SIZE * 2]; + + int32_t mainSort__copyStart[256]; + int32_t mainSort__copyEnd[256]; +} EState; + + +/*-- compression. --*/ + +static int32_t +BZ2_blockSort(EState*); + +static void +BZ2_compressBlock(EState*, int); + +static void +BZ2_bsInitWrite(EState*); + +static void +BZ2_hbAssignCodes(int32_t*, uint8_t*, int32_t, int32_t, int32_t); + +static void +BZ2_hbMakeCodeLengths(EState*, uint8_t*, int32_t*, int32_t, int32_t); + +/*-------------------------------------------------------------*/ +/*--- end bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/busybox/archival/libarchive/bz/compress.c b/busybox/archival/libarchive/bz/compress.c new file mode 100644 index 00000000000000..539ab927e2680e --- /dev/null +++ b/busybox/archival/libarchive/bz/compress.c @@ -0,0 +1,752 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting) ---*/ +/*--- compress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +/* CHANGES + * 0.9.0 -- original version. + * 0.9.0a/b -- no changes in this file. + * 0.9.0c -- changed setting of nGroups in sendMTFValues() + * so as to do a bit better on small files +*/ + +/* #include "bzlib_private.h" */ + +#if BZIP2_SPEED >= 5 +# define ALWAYS_INLINE_5 ALWAYS_INLINE +#else +# define ALWAYS_INLINE_5 /*nothing*/ +#endif + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void BZ2_bsInitWrite(EState* s) +{ + s->bsLive = 0; + s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static NOINLINE +void bsFinishWrite(EState* s) +{ + while (s->bsLive > 0) { + *s->posZ++ = (uint8_t)(s->bsBuff >> 24); + s->bsBuff <<= 8; + s->bsLive -= 8; + } +} + + +/*---------------------------------------------------*/ +static +/* Helps only on level 5, on other levels hurts. ? */ +ALWAYS_INLINE_5 +void bsW(EState* s, int32_t n, uint32_t v) +{ + while (s->bsLive >= 8) { + *s->posZ++ = (uint8_t)(s->bsBuff >> 24); + s->bsBuff <<= 8; + s->bsLive -= 8; + } + s->bsBuff |= (v << (32 - s->bsLive - n)); + s->bsLive += n; +} +/* Same with n == 16: */ +static +ALWAYS_INLINE_5 +void bsW16(EState* s, uint32_t v) +{ + while (s->bsLive >= 8) { + *s->posZ++ = (uint8_t)(s->bsBuff >> 24); + s->bsBuff <<= 8; + s->bsLive -= 8; + } + s->bsBuff |= (v << (16 - s->bsLive)); + s->bsLive += 16; +} +/* Same with n == 1: */ +static +ALWAYS_INLINE /* one callsite */ +void bsW1_1(EState* s) +{ + /* need space for only 1 bit, no need for loop freeing > 8 bits */ + if (s->bsLive >= 8) { + *s->posZ++ = (uint8_t)(s->bsBuff >> 24); + s->bsBuff <<= 8; + s->bsLive -= 8; + } + s->bsBuff |= (1 << (31 - s->bsLive)); + s->bsLive += 1; +} +static +ALWAYS_INLINE_5 +void bsW1_0(EState* s) +{ + /* need space for only 1 bit, no need for loop freeing > 8 bits */ + if (s->bsLive >= 8) { + *s->posZ++ = (uint8_t)(s->bsBuff >> 24); + s->bsBuff <<= 8; + s->bsLive -= 8; + } + //s->bsBuff |= (0 << (31 - s->bsLive)); + s->bsLive += 1; +} + + +/*---------------------------------------------------*/ +static ALWAYS_INLINE +void bsPutU16(EState* s, unsigned u) +{ + bsW16(s, u); +} + + +/*---------------------------------------------------*/ +static +void bsPutU32(EState* s, unsigned u) +{ + //bsW(s, 32, u); // can't use: may try "uint32 << -n" + bsW16(s, (u >> 16) & 0xffff); + bsW16(s, u & 0xffff); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e(EState* s) +{ + int i; + unsigned cnt = 0; + for (i = 0; i < 256; i++) { + if (s->inUse[i]) { + s->unseqToSeq[i] = cnt; + cnt++; + } + } + s->nInUse = cnt; +} + + +/*---------------------------------------------------*/ +/* + * This bit of code is performance-critical. + * On 32bit x86, gcc-6.3.0 was observed to spill ryy_j to stack, + * resulting in abysmal performance (x3 slowdown). + * Forcing it into a separate function alleviates register pressure, + * and spillage no longer happens. + * Other versions of gcc do not exhibit this problem, but out-of-line code + * seems to be helping them too (code is both smaller and faster). + * Therefore NOINLINE is enabled for the entire 32bit x86 arch for now, + * without a check for gcc version. + */ +static +#if defined __i386__ +NOINLINE +#endif +int inner_loop(uint8_t *yy, uint8_t ll_i) +{ + register uint8_t rtmp; + register uint8_t* ryy_j; + rtmp = yy[1]; + yy[1] = yy[0]; + ryy_j = &(yy[1]); + while (ll_i != rtmp) { + register uint8_t rtmp2; + ryy_j++; + rtmp2 = rtmp; + rtmp = *ryy_j; + *ryy_j = rtmp2; + } + yy[0] = rtmp; + return ryy_j - &(yy[0]); +} +static NOINLINE +void generateMTFValues(EState* s) +{ + uint8_t yy[256]; + int i; + int zPend; + int32_t wr; + + /* + * After sorting (eg, here), + * s->arr1[0 .. s->nblock-1] holds sorted order, + * and + * ((uint8_t*)s->arr2)[0 .. s->nblock-1] + * holds the original block data. + * + * The first thing to do is generate the MTF values, + * and put them in ((uint16_t*)s->arr1)[0 .. s->nblock-1]. + * + * Because there are strictly fewer or equal MTF values + * than block values, ptr values in this area are overwritten + * with MTF values only when they are no longer needed. + * + * The final compressed bitstream is generated into the + * area starting at &((uint8_t*)s->arr2)[s->nblock] + * + * These storage aliases are set up in bzCompressInit(), + * except for the last one, which is arranged in + * compressBlock(). + */ + uint32_t* ptr = s->ptr; + + makeMaps_e(s); + + wr = 0; + zPend = 0; + for (i = 0; i <= s->nInUse+1; i++) + s->mtfFreq[i] = 0; + + for (i = 0; i < s->nInUse; i++) + yy[i] = (uint8_t) i; + + for (i = 0; i < s->nblock; i++) { + uint8_t ll_i = ll_i; /* gcc 4.3.1 thinks it may be used w/o init */ + int32_t j; + + AssertD(wr <= i, "generateMTFValues(1)"); + j = ptr[i] - 1; + if (j < 0) + j += s->nblock; + ll_i = s->unseqToSeq[s->block[j]]; + AssertD(ll_i < s->nInUse, "generateMTFValues(2a)"); + + if (yy[0] == ll_i) { + zPend++; + continue; + } + + if (zPend > 0) { + process_zPend: + zPend--; + while (1) { +#if 0 + if (zPend & 1) { + s->mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + s->mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } +#else /* same as above, since BZ_RUNA is 0 and BZ_RUNB is 1 */ + unsigned run = zPend & 1; + s->mtfv[wr] = run; + wr++; + s->mtfFreq[run]++; +#endif + zPend -= 2; + if (zPend < 0) + break; + zPend = (unsigned)zPend / 2; + /* bbox: unsigned div is easier */ + } + if (i < 0) /* came via "goto process_zPend"? exit */ + goto end; + zPend = 0; + } + j = inner_loop(yy, ll_i); + s->mtfv[wr] = j+1; + wr++; + s->mtfFreq[j+1]++; + } + + i = -1; + if (zPend > 0) + goto process_zPend; /* "process it and come back here" */ + end: + s->mtfv[wr] = s->nInUse+1; + wr++; + s->mtfFreq[s->nInUse+1]++; + + s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST 0 +#define BZ_GREATER_ICOST 15 + +static NOINLINE +void sendMTFValues(EState* s) +{ + int32_t t, i; + unsigned iter; + unsigned gs; + int32_t alphaSize; + unsigned nSelectors, selCtr; + int32_t nGroups; + + /* + * uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + * is a global since the decoder also needs it. + * + * int32_t code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + * int32_t rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + * are also globals only used in this proc. + * Made global to keep stack frame size small. + */ +#define code sendMTFValues__code +#define rfreq sendMTFValues__rfreq +#define len_pack sendMTFValues__len_pack + + unsigned /*uint16_t*/ cost[BZ_N_GROUPS]; + + uint16_t* mtfv = s->mtfv; + + alphaSize = s->nInUse + 2; + for (t = 0; t < BZ_N_GROUPS; t++) { + unsigned v; + for (v = 0; v < alphaSize; v++) + s->len[t][v] = BZ_GREATER_ICOST; + } + + /*--- Decide how many coding tables to use ---*/ + AssertH(s->nMTF > 0, 3001); + // 1..199 = 2 + // 200..599 = 3 + // 600..1199 = 4 + // 1200..2399 = 5 + // 2400..99999 = 6 + nGroups = 2; + nGroups += (s->nMTF >= 200); + nGroups += (s->nMTF >= 600); + nGroups += (s->nMTF >= 1200); + nGroups += (s->nMTF >= 2400); + + /*--- Generate an initial set of coding tables ---*/ + { + unsigned nPart, remF; + + nPart = nGroups; + remF = s->nMTF; + gs = 0; + while (nPart > 0) { + unsigned v; + unsigned ge; + unsigned tFreq, aFreq; + + tFreq = remF / nPart; + ge = gs; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize) { + aFreq += s->mtfFreq[ge++]; + } + ge--; + + if (ge > gs + && nPart != nGroups && nPart != 1 + && ((nGroups - nPart) % 2 == 1) /* bbox: can this be replaced by x & 1? */ + ) { + aFreq -= s->mtfFreq[ge]; + ge--; + } + + for (v = 0; v < alphaSize; v++) + if (v >= gs && v <= ge) + s->len[nPart-1][v] = BZ_LESSER_ICOST; + else + s->len[nPart-1][v] = BZ_GREATER_ICOST; + + nPart--; + gs = ge + 1; + remF -= aFreq; + } + } + + /* + * Iterate up to BZ_N_ITERS times to improve the tables. + */ + for (iter = 0; iter < BZ_N_ITERS; iter++) { + for (t = 0; t < nGroups; t++) { + unsigned v; + for (v = 0; v < alphaSize; v++) + s->rfreq[t][v] = 0; + } + +#if BZIP2_SPEED >= 5 + /* + * Set up an auxiliary length table which is used to fast-track + * the common case (nGroups == 6). + */ + if (nGroups == 6) { + unsigned v; + for (v = 0; v < alphaSize; v++) { + s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; + s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; + s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; + } + } +#endif + nSelectors = 0; + gs = 0; + while (1) { + unsigned ge; + unsigned bt, bc; + + /*--- Set group start & end marks. --*/ + if (gs >= s->nMTF) + break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) + ge = s->nMTF-1; + + /* + * Calculate the cost of this group as coded + * by each of the coding tables. + */ + for (t = 0; t < nGroups; t++) + cost[t] = 0; +#if BZIP2_SPEED >= 5 + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + register uint32_t cost01, cost23, cost45; + register uint16_t icv; + cost01 = cost23 = cost45 = 0; +#define BZ_ITER(nn) \ + icv = mtfv[gs+(nn)]; \ + cost01 += s->len_pack[icv][0]; \ + cost23 += s->len_pack[icv][1]; \ + cost45 += s->len_pack[icv][2]; + BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4); + BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9); + BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); + BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); + BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); + BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); + BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); + BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); + BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); + BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); +#undef BZ_ITER + 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 + { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + unsigned /*uint16_t*/ icv = mtfv[i]; + for (t = 0; t < nGroups; t++) + cost[t] += s->len[t][icv]; + } + } + /* + * Find the coding table which is best for this group, + * and record its identity in the selector table. + */ + /*bc = 999999999;*/ + /*bt = -1;*/ + bc = cost[0]; + bt = 0; + for (t = 1 /*0*/; t < nGroups; t++) { + if (cost[t] < bc) { + bc = cost[t]; + bt = t; + } + } + s->selector[nSelectors] = bt; + nSelectors++; + + /* + * Increment the symbol frequencies for the selected table. + */ +/* 1% faster compress. +800 bytes */ +#if BZIP2_SPEED >= 4 + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ +#define BZ_ITUR(nn) s->rfreq[bt][mtfv[gs + (nn)]]++ + BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4); + BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9); + BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); + BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); + BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); + BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); + BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); + BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); + BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); + BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); +#undef BZ_ITUR + gs = ge + 1; + } else +#endif + { + /*--- slow version which correctly handles all situations ---*/ + while (gs <= ge) { + s->rfreq[bt][mtfv[gs]]++; + gs++; + } + /* already is: gs = ge + 1; */ + } + } + + /* + * Recompute the tables based on the accumulated frequencies. + */ + /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See + * comment in huffman.c for details. */ + for (t = 0; t < nGroups; t++) + BZ2_hbMakeCodeLengths(s, &(s->len[t][0]), &(s->rfreq[t][0]), alphaSize, 17 /*20*/); + } + + AssertH(nGroups < 8, 3002); + AssertH(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZ_G_SIZE)), 3003); + + /*--- Compute MTF values for the selectors. ---*/ + { + uint8_t pos[BZ_N_GROUPS], ll_i, tmp2, tmp; + + for (i = 0; i < nGroups; i++) + pos[i] = i; + for (i = 0; i < nSelectors; i++) { + unsigned j; + ll_i = s->selector[i]; + j = 0; + tmp = pos[j]; + while (ll_i != tmp) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + } + pos[0] = tmp; + s->selectorMtf[i] = j; + } + } + + /*--- Assign actual codes for the tables. --*/ + for (t = 0; t < nGroups; t++) { + unsigned minLen = 32; //todo: s->len[t][0]; + unsigned maxLen = 0; //todo: s->len[t][0]; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + AssertH(!(maxLen > 17 /*20*/), 3004); + AssertH(!(minLen < 1), 3005); + BZ2_hbAssignCodes(&(s->code[t][0]), &(s->len[t][0]), minLen, maxLen, alphaSize); + } + + /*--- Transmit the mapping table. ---*/ + { + /* bbox: optimized a bit more than in bzip2 */ + int inUse16 = 0; + for (i = 0; i < 16; i++) { + if (sizeof(long) <= 4) { + inUse16 = inUse16*2 + + ((*(bb__aliased_uint32_t*)&(s->inUse[i * 16 + 0]) + | *(bb__aliased_uint32_t*)&(s->inUse[i * 16 + 4]) + | *(bb__aliased_uint32_t*)&(s->inUse[i * 16 + 8]) + | *(bb__aliased_uint32_t*)&(s->inUse[i * 16 + 12])) != 0); + } else { /* Our CPU can do better */ + inUse16 = inUse16*2 + + ((*(bb__aliased_uint64_t*)&(s->inUse[i * 16 + 0]) + | *(bb__aliased_uint64_t*)&(s->inUse[i * 16 + 8])) != 0); + } + } + + bsW16(s, inUse16); + + inUse16 <<= (sizeof(int)*8 - 16); /* move 15th bit into sign bit */ + for (i = 0; i < 16; i++) { + if (inUse16 < 0) { + unsigned v16 = 0; + unsigned j; + for (j = 0; j < 16; j++) + v16 = v16*2 + s->inUse[i * 16 + j]; + bsW16(s, v16); + } + inUse16 <<= 1; + } + } + + /*--- Now the selectors. ---*/ + bsW(s, 3, nGroups); + bsW(s, 15, nSelectors); + for (i = 0; i < nSelectors; i++) { + unsigned j; + for (j = 0; j < s->selectorMtf[i]; j++) + bsW1_1(s); + bsW1_0(s); + } + + /*--- Now the coding tables. ---*/ + for (t = 0; t < nGroups; t++) { + unsigned curr = s->len[t][0]; + bsW(s, 5, curr); + for (i = 0; i < alphaSize; i++) { + while (curr < s->len[t][i]) { bsW(s, 2, 2); curr++; /* 10 */ } + while (curr > s->len[t][i]) { bsW(s, 2, 3); curr--; /* 11 */ } + bsW1_0(s); + } + } + + /*--- And finally, the block data proper ---*/ + selCtr = 0; + gs = 0; + while (1) { + unsigned ge; + + if (gs >= s->nMTF) + break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) + ge = s->nMTF-1; + AssertH(s->selector[selCtr] < nGroups, 3006); + +/* Costs 1300 bytes and is _slower_ (on Intel Core 2) */ +#if 0 + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + uint16_t mtfv_i; + uint8_t* s_len_sel_selCtr = &(s->len[s->selector[selCtr]][0]); + int32_t* s_code_sel_selCtr = &(s->code[s->selector[selCtr]][0]); +#define BZ_ITAH(nn) \ + mtfv_i = mtfv[gs+(nn)]; \ + bsW(s, s_len_sel_selCtr[mtfv_i], s_code_sel_selCtr[mtfv_i]) + BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4); + BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9); + BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); + BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); + BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); + BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); + BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); + BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); + BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); + BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); +#undef BZ_ITAH + gs = ge+1; + } else +#endif + { + /*--- slow version which correctly handles all situations ---*/ + /* code is bit bigger, but moves multiply out of the loop */ + uint8_t* s_len_sel_selCtr = &(s->len [s->selector[selCtr]][0]); + int32_t* s_code_sel_selCtr = &(s->code[s->selector[selCtr]][0]); + while (gs <= ge) { + bsW(s, + s_len_sel_selCtr[mtfv[gs]], + s_code_sel_selCtr[mtfv[gs]] + ); + gs++; + } + /* already is: gs = ge+1; */ + } + selCtr++; + } + AssertH(selCtr == nSelectors, 3007); +#undef code +#undef rfreq +#undef len_pack +} + + +/*---------------------------------------------------*/ +static +void BZ2_compressBlock(EState* s, int is_last_block) +{ + int32_t origPtr = origPtr; + + if (s->nblock > 0) { + BZ_FINALISE_CRC(s->blockCRC); + s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); + s->combinedCRC ^= s->blockCRC; + if (s->blockNo > 1) + s->posZ = s->zbits; // was: s->numZ = 0; + + origPtr = BZ2_blockSort(s); + } + + s->zbits = &((uint8_t*)s->arr2)[s->nblock]; + s->posZ = s->zbits; + s->state_out_pos = s->zbits; + + /*-- If this is the first block, create the stream header. --*/ + if (s->blockNo == 1) { + BZ2_bsInitWrite(s); + /*bsPutU8(s, BZ_HDR_B);*/ + /*bsPutU8(s, BZ_HDR_Z);*/ + /*bsPutU8(s, BZ_HDR_h);*/ + /*bsPutU8(s, BZ_HDR_0 + s->blockSize100k);*/ + bsPutU32(s, BZ_HDR_BZh0 + s->blockSize100k); + } + + if (s->nblock > 0) { + /*bsPutU8(s, 0x31);*/ + /*bsPutU8(s, 0x41);*/ + /*bsPutU8(s, 0x59);*/ + /*bsPutU8(s, 0x26);*/ + bsPutU32(s, 0x31415926); + /*bsPutU8(s, 0x53);*/ + /*bsPutU8(s, 0x59);*/ + bsPutU16(s, 0x5359); + + /*-- Now the block's CRC, so it is in a known place. --*/ + bsPutU32(s, s->blockCRC); + + /* + * Now a single bit indicating (non-)randomisation. + * As of version 0.9.5, we use a better sorting algorithm + * which makes randomisation unnecessary. So always set + * the randomised bit to 'no'. Of course, the decoder + * still needs to be able to handle randomised blocks + * so as to maintain backwards compatibility with + * older versions of bzip2. + */ + bsW1_0(s); + + bsW(s, 24, origPtr); + generateMTFValues(s); + sendMTFValues(s); + } + + /*-- If this is the last block, add the stream trailer. --*/ + if (is_last_block) { + /*bsPutU8(s, 0x17);*/ + /*bsPutU8(s, 0x72);*/ + /*bsPutU8(s, 0x45);*/ + /*bsPutU8(s, 0x38);*/ + bsPutU32(s, 0x17724538); + /*bsPutU8(s, 0x50);*/ + /*bsPutU8(s, 0x90);*/ + bsPutU16(s, 0x5090); + bsPutU32(s, s->combinedCRC); + bsFinishWrite(s); + } +} + + +/*-------------------------------------------------------------*/ +/*--- end compress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/busybox/archival/libarchive/bz/huffman.c b/busybox/archival/libarchive/bz/huffman.c new file mode 100644 index 00000000000000..dc851cd3fb407e --- /dev/null +++ b/busybox/archival/libarchive/bz/huffman.c @@ -0,0 +1,229 @@ +/* + * bzip2 is written by Julian Seward . + * Adapted for busybox by Denys Vlasenko . + * See README and LICENSE files in this directory for more information. + */ + +/*-------------------------------------------------------------*/ +/*--- Huffman coding low-level stuff ---*/ +/*--- huffman.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +This file is part of bzip2/libbzip2, a program and library for +lossless, block-sorting data compression. + +bzip2/libbzip2 version 1.0.4 of 20 December 2006 +Copyright (C) 1996-2006 Julian Seward + +Please read the WARNING, DISCLAIMER and PATENTS sections in the +README file. + +This program is released under the terms of the license contained +in the file LICENSE. +------------------------------------------------------------------ */ + +/* #include "bzlib_private.h" */ + +/*---------------------------------------------------*/ +#define WEIGHTOF(zz0) ((zz0) & 0xffffff00) +#define DEPTHOF(zz1) ((zz1) & 0x000000ff) +#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3)) + +#define ADDWEIGHTS(zw1,zw2) \ + (WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \ + (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2))) + +#define UPHEAP(z) \ +{ \ + int32_t zz, tmp; \ + zz = z; \ + tmp = heap[zz]; \ + while (weight[tmp] < weight[heap[zz >> 1]]) { \ + heap[zz] = heap[zz >> 1]; \ + zz >>= 1; \ + } \ + heap[zz] = tmp; \ +} + + +/* 90 bytes, 0.3% of overall compress speed */ +#if BZIP2_SPEED >= 1 + +/* macro works better than inline (gcc 4.2.1) */ +#define DOWNHEAP1(heap, weight, Heap) \ +{ \ + int32_t zz, yy, tmp; \ + zz = 1; \ + tmp = heap[zz]; \ + while (1) { \ + yy = zz << 1; \ + if (yy > nHeap) \ + break; \ + if (yy < nHeap \ + && weight[heap[yy+1]] < weight[heap[yy]]) \ + yy++; \ + if (weight[tmp] < weight[heap[yy]]) \ + break; \ + heap[zz] = heap[yy]; \ + zz = yy; \ + } \ + heap[zz] = tmp; \ +} + +#else + +static +void DOWNHEAP1(int32_t *heap, int32_t *weight, int32_t nHeap) +{ + int32_t zz, yy, tmp; + zz = 1; + tmp = heap[zz]; + while (1) { + yy = zz << 1; + if (yy > nHeap) + break; + if (yy < nHeap + && weight[heap[yy + 1]] < weight[heap[yy]]) + yy++; + if (weight[tmp] < weight[heap[yy]]) + break; + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; +} + +#endif + +/*---------------------------------------------------*/ +static +void BZ2_hbMakeCodeLengths(EState *s, + uint8_t *len, + int32_t *freq, + int32_t alphaSize, + int32_t maxLen) +{ + /* + * Nodes and heap entries run from 1. Entry 0 + * for both the heap and nodes is a sentinel. + */ + int32_t nNodes, nHeap, n1, n2, i, j, k; + Bool tooLong; + + /* bbox: moved to EState to save stack + int32_t heap [BZ_MAX_ALPHA_SIZE + 2]; + int32_t weight[BZ_MAX_ALPHA_SIZE * 2]; + int32_t parent[BZ_MAX_ALPHA_SIZE * 2]; + */ +#define heap (s->BZ2_hbMakeCodeLengths__heap) +#define weight (s->BZ2_hbMakeCodeLengths__weight) +#define parent (s->BZ2_hbMakeCodeLengths__parent) + + for (i = 0; i < alphaSize; i++) + weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + + while (1) { + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + UPHEAP(nHeap); + } + + AssertH(nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001); + + while (nHeap > 1) { + n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP1(heap, weight, nHeap); + n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP1(heap, weight, nHeap); + nNodes++; + parent[n1] = parent[n2] = nNodes; + weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]); + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + UPHEAP(nHeap); + } + + AssertH(nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002); + + tooLong = False; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { + k = parent[k]; + j++; + } + len[i-1] = j; + if (j > maxLen) + tooLong = True; + } + + if (!tooLong) + break; + + /* 17 Oct 04: keep-going condition for the following loop used + to be 'i < alphaSize', which missed the last element, + theoretically leading to the possibility of the compressor + looping. However, this count-scaling step is only needed if + one of the generated Huffman code words is longer than + maxLen, which up to and including version 1.0.2 was 20 bits, + which is extremely unlikely. In version 1.0.3 maxLen was + changed to 17 bits, which has minimal effect on compression + ratio, but does mean this scaling step is used from time to + time, enough to verify that it works. + + This means that bzip2-1.0.3 and later will only produce + Huffman codes with a maximum length of 17 bits. However, in + order to preserve backwards compatibility with bitstreams + produced by versions pre-1.0.3, the decompressor must still + handle lengths of up to 20. */ + + for (i = 1; i <= alphaSize; i++) { + j = weight[i] >> 8; + /* bbox: yes, it is a signed division. + * don't replace with shift! */ + j = 1 + (j / 2); + weight[i] = j << 8; + } + } +#undef heap +#undef weight +#undef parent +} + + +/*---------------------------------------------------*/ +static +void BZ2_hbAssignCodes(int32_t *code, + uint8_t *length, + int32_t minLen, + int32_t maxLen, + int32_t alphaSize) +{ + int32_t n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) { + if (length[i] == n) { + code[i] = vec; + vec++; + } + } + vec <<= 1; + } +} + + +/*-------------------------------------------------------------*/ +/*--- end huffman.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/busybox/archival/libarchive/common.c b/busybox/archival/libarchive/common.c new file mode 100644 index 00000000000000..25c7bddad0adde --- /dev/null +++ b/busybox/archival/libarchive/common.c @@ -0,0 +1,8 @@ +/* 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/busybox/archival/libarchive/data_align.c b/busybox/archival/libarchive/data_align.c new file mode 100644 index 00000000000000..f61fdd93fda3fb --- /dev/null +++ b/busybox/archival/libarchive/data_align.c @@ -0,0 +1,14 @@ +/* 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" + +void FAST_FUNC data_align(archive_handle_t *archive_handle, unsigned boundary) +{ + unsigned skip_amount = (boundary - (archive_handle->offset % boundary)) % boundary; + + archive_handle->seek(archive_handle->src_fd, skip_amount); + archive_handle->offset += skip_amount; +} diff --git a/busybox/archival/libarchive/data_extract_all.c b/busybox/archival/libarchive/data_extract_all.c new file mode 100644 index 00000000000000..4c95db4a66dfbe --- /dev/null +++ b/busybox/archival/libarchive/data_extract_all.c @@ -0,0 +1,255 @@ +/* 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" + +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]; + if (!sctx) + sctx = archive_handle->tar__sctx[PAX_GLOBAL]; + if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */ + setfscreatecon(sctx); + free(archive_handle->tar__sctx[PAX_NEXT_FILE]); + archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL; + } +#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(dst_name, '/'); + if (slash) { + *slash = '\0'; + bb_make_directory(dst_name, -1, FILEUTILS_RECUR); + *slash = '/'; + } + } + + if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) { + /* Remove the entry if it exists */ + if (!S_ISDIR(file_header->mode)) { + if (hard_link) { + /* Ugly special case: + * tar cf t.tar hardlink1 hardlink2 hardlink1 + * results in this tarball structure: + * hardlink1 + * hardlink2 -> hardlink1 + * hardlink1 -> hardlink1 <== !!! + */ + if (strcmp(hard_link, dst_name) == 0) + goto ret; + } + /* Proceed with deleting */ + if (unlink(dst_name) == -1 + && errno != ENOENT + ) { + bb_perror_msg_and_die("can't remove old file %s", + 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(dst_name, &existing_sb) == -1) { + if (errno != ENOENT) { + bb_perror_msg_and_die("can't stat old file"); + } + } + else if (existing_sb.st_mtime >= file_header->mtime) { + if (!S_ISDIR(file_header->mode)) { + bb_error_msg("%s not created: newer or " + "same age file exists", dst_name); + } + data_skip(archive_handle); + goto ret; + } + else if ((unlink(dst_name) == -1) && (errno != EISDIR)) { + bb_perror_msg_and_die("can't remove old file %s", + dst_name); + } + } + + /* Handle hard links separately */ + if (hard_link) { + create_or_remember_link(&archive_handle->link_placeholders, + hard_link, + dst_name, + 1); + /* Hardlinks have no separate mode/ownership, skip chown/chmod */ + goto ret; + } + + /* Create the filesystem entry */ + switch (file_header->mode & S_IFMT) { + case S_IFREG: { + /* Regular file */ + 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_nameN = dst_name; +#ifdef ARCHIVE_REPLACE_VIA_RENAME + if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) + /* rpm-style temp file name */ + dst_nameN = xasprintf("%s;%x", dst_name, (int)getpid()); +#endif + dst_fd = xopen3(dst_nameN, + flags, + file_header->mode + ); + bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size); + close(dst_fd); +#ifdef ARCHIVE_REPLACE_VIA_RENAME + if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) { + xrename(dst_nameN, dst_name); + free(dst_nameN); + } +#endif + break; + } + case S_IFDIR: + res = mkdir(dst_name, file_header->mode); + if ((res != 0) + && (errno != EISDIR) /* btw, Linux doesn't return this */ + && (errno != EEXIST) + ) { + 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?) + + /* To avoid a directory traversal attack via symlinks, + * do not restore symlinks with ".." components + * or symlinks starting with "/", unless a magic + * envvar is set. + * + * For example, consider a .tar created via: + * $ tar cvf bug.tar anything.txt + * $ ln -s /tmp symlink + * $ tar --append -f bug.tar symlink + * $ rm symlink + * $ mkdir symlink + * $ tar --append -f bug.tar symlink/evil.py + * + * This will result in an archive that contains: + * $ tar --list -f bug.tar + * anything.txt + * symlink [-> /tmp] + * symlink/evil.py + * + * Untarring bug.tar would otherwise place evil.py in '/tmp'. + */ + create_or_remember_link(&archive_handle->link_placeholders, + file_header->link_target, + dst_name, + 0); + break; + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + res = mknod(dst_name, file_header->mode, file_header->device); + if (res != 0) { + bb_perror_msg("can't create node %s", dst_name); + } + break; + default: + bb_error_msg_and_die("unrecognized file type"); + } + + if (!S_ISLNK(file_header->mode)) { + if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) { + uid_t uid = file_header->uid; + gid_t gid = file_header->gid; +#if ENABLE_FEATURE_TAR_UNAME_GNAME + if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) { + if (file_header->tar__uname) { +//TODO: cache last name/id pair? + struct passwd *pwd = getpwnam(file_header->tar__uname); + if (pwd) uid = pwd->pw_uid; + } + if (file_header->tar__gname) { + struct group *grp = getgrnam(file_header->tar__gname); + if (grp) gid = grp->gr_gid; + } + } +#endif + /* GNU tar 1.15.1 uses chown, not lchown */ + 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(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(dst_name, t); + } + } + + ret: ; +#if ENABLE_FEATURE_TAR_SELINUX + if (sctx) { + /* reset the context after creating an entry */ + setfscreatecon(NULL); + } +#endif +} diff --git a/busybox/archival/libarchive/data_extract_to_command.c b/busybox/archival/libarchive/data_extract_to_command.c new file mode 100644 index 00000000000000..0fcabb4a987b7a --- /dev/null +++ b/busybox/archival/libarchive/data_extract_to_command.c @@ -0,0 +1,136 @@ +/* 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" + +enum { + //TAR_FILETYPE, + TAR_MODE, + TAR_FILENAME, + TAR_REALNAME, +#if ENABLE_FEATURE_TAR_UNAME_GNAME + TAR_UNAME, + TAR_GNAME, +#endif + TAR_SIZE, + TAR_UID, + TAR_GID, + TAR_MAX, +}; + +static const char *const tar_var[] = { + // "FILETYPE", + "MODE", + "FILENAME", + "REALNAME", +#if ENABLE_FEATURE_TAR_UNAME_GNAME + "UNAME", + "GNAME", +#endif + "SIZE", + "UID", + "GID", +}; + +static void xputenv(char *str) +{ + if (putenv(str)) + bb_die_memory_exhausted(); +} + +static void str2env(char *env[], int idx, const char *str) +{ + env[idx] = xasprintf("TAR_%s=%s", tar_var[idx], str); + xputenv(env[idx]); +} + +static void dec2env(char *env[], int idx, unsigned long long val) +{ + env[idx] = xasprintf("TAR_%s=%llu", tar_var[idx], val); + xputenv(env[idx]); +} + +static void oct2env(char *env[], int idx, unsigned long val) +{ + env[idx] = xasprintf("TAR_%s=%lo", tar_var[idx], val); + xputenv(env[idx]); +} + +void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle) +{ + file_header_t *file_header = archive_handle->file_header; + +#if 0 /* do we need this? ENABLE_FEATURE_TAR_SELINUX */ + char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE]; + if (!sctx) + sctx = archive_handle->tar__sctx[PAX_GLOBAL]; + if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */ + setfscreatecon(sctx); + free(archive_handle->tar__sctx[PAX_NEXT_FILE]); + archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL; + } +#endif + + if ((file_header->mode & S_IFMT) == S_IFREG) { + pid_t pid; + int p[2], status; + char *tar_env[TAR_MAX]; + + memset(tar_env, 0, sizeof(tar_env)); + + xpipe(p); + pid = BB_MMU ? xfork() : xvfork(); + if (pid == 0) { + /* Child */ + /* str2env(tar_env, TAR_FILETYPE, "f"); - parent should do it once */ + oct2env(tar_env, TAR_MODE, file_header->mode); + str2env(tar_env, TAR_FILENAME, file_header->name); + str2env(tar_env, TAR_REALNAME, file_header->name); +#if ENABLE_FEATURE_TAR_UNAME_GNAME + str2env(tar_env, TAR_UNAME, file_header->tar__uname); + str2env(tar_env, TAR_GNAME, file_header->tar__gname); +#endif + dec2env(tar_env, TAR_SIZE, file_header->size); + dec2env(tar_env, TAR_UID, file_header->uid); + dec2env(tar_env, TAR_GID, file_header->gid); + close(p[1]); + xdup2(p[0], STDIN_FILENO); + signal(SIGPIPE, SIG_DFL); + execl(archive_handle->tar__to_command_shell, + archive_handle->tar__to_command_shell, + "-c", + archive_handle->tar__to_command, + (char *)0); + bb_perror_msg_and_die("can't execute '%s'", archive_handle->tar__to_command_shell); + } + close(p[0]); + /* Our caller is expected to do signal(SIGPIPE, SIG_IGN) + * so that we don't die if child don't read all the input: */ + bb_copyfd_exact_size(archive_handle->src_fd, p[1], -file_header->size); + close(p[1]); + + 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 by signal %d", + archive_handle->tar__to_command, WTERMSIG(status)); + + if (!BB_MMU) { + int i; + for (i = 0; i < TAR_MAX; i++) { + if (tar_env[i]) + bb_unsetenv_and_free(tar_env[i]); + } + } + } + +#if 0 /* ENABLE_FEATURE_TAR_SELINUX */ + if (sctx) + /* reset the context after creating an entry */ + setfscreatecon(NULL); +#endif +} diff --git a/busybox/archival/libarchive/data_extract_to_stdout.c b/busybox/archival/libarchive/data_extract_to_stdout.c new file mode 100644 index 00000000000000..520041329809ab --- /dev/null +++ b/busybox/archival/libarchive/data_extract_to_stdout.c @@ -0,0 +1,13 @@ +/* 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" + +void FAST_FUNC data_extract_to_stdout(archive_handle_t *archive_handle) +{ + bb_copyfd_exact_size(archive_handle->src_fd, + STDOUT_FILENO, + archive_handle->file_header->size); +} diff --git a/busybox/archival/libarchive/data_skip.c b/busybox/archival/libarchive/data_skip.c new file mode 100644 index 00000000000000..1a608227e06646 --- /dev/null +++ b/busybox/archival/libarchive/data_skip.c @@ -0,0 +1,11 @@ +/* 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" + +void FAST_FUNC data_skip(archive_handle_t *archive_handle) +{ + archive_handle->seek(archive_handle->src_fd, archive_handle->file_header->size); +} diff --git a/busybox/archival/libarchive/decompress_bunzip2.c b/busybox/archival/libarchive/decompress_bunzip2.c new file mode 100644 index 00000000000000..7ef4e035f32ff8 --- /dev/null +++ b/busybox/archival/libarchive/decompress_bunzip2.c @@ -0,0 +1,870 @@ +/* vi: set sw=4 ts=4: */ +/* + * Small bzip2 deflate implementation, by Rob Landley (rob@landley.net). + * + * Based on bzip2 decompression code by Julian R Seward (jseward@acm.org), + * which also acknowledges contributions by Mike Burrows, David Wheeler, + * Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten, + * Robert Sedgewick, and Jon L. Bentley. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* + Size and speed optimizations by Manuel Novoa III (mjn3@codepoet.org). + + More efficient reading of Huffman codes, a streamlined read_bunzip() + function, and various other tweaks. In (limited) tests, approximately + 20% faster than bzcat on x86 and about 10% faster on arm. + + Note that about 2/3 of the time is spent in read_bunzip() reversing + the Burrows-Wheeler transformation. Much of that time is delay + resulting from cache misses. + + (2010 update by vda: profiled "bzcat <84mbyte.bz2 >/dev/null" + on x86-64 CPU with L2 > 1M: get_next_block is hotter than read_bunzip: + %time seconds calls function + 71.01 12.69 444 get_next_block + 28.65 5.12 93065 read_bunzip + 00.22 0.04 7736490 get_bits + 00.11 0.02 47 dealloc_bunzip + 00.00 0.00 93018 full_write + ...) + + + I would ask that anyone benefiting from this work, especially those + using it in commercial products, consider making a donation to my local + non-profit hospice organization (www.hospiceacadiana.com) in the name of + the woman I loved, Toni W. Hagan, who passed away Feb. 12, 2003. + + Manuel + */ +#include "libbb.h" +#include "bb_archive.h" + +#if 0 +# define dbg(...) bb_error_msg(__VA_ARGS__) +#else +# define dbg(...) ((void)0) +#endif + +/* Constants for Huffman coding */ +#define MAX_GROUPS 6 +#define GROUP_SIZE 50 /* 64 would have been more efficient */ +#define MAX_HUFCODE_BITS 20 /* Longest Huffman code allowed */ +#define MAX_SYMBOLS 258 /* 256 literals + RUNA + RUNB */ +#define SYMBOL_RUNA 0 +#define SYMBOL_RUNB 1 + +/* Status return values */ +#define RETVAL_OK 0 +#define RETVAL_LAST_BLOCK (dbg("%d", __LINE__), -1) +#define RETVAL_NOT_BZIP_DATA (dbg("%d", __LINE__), -2) +#define RETVAL_UNEXPECTED_INPUT_EOF (dbg("%d", __LINE__), -3) +#define RETVAL_SHORT_WRITE (dbg("%d", __LINE__), -4) +#define RETVAL_DATA_ERROR (dbg("%d", __LINE__), -5) +#define RETVAL_OUT_OF_MEMORY (dbg("%d", __LINE__), -6) +#define RETVAL_OBSOLETE_INPUT (dbg("%d", __LINE__), -7) + +/* Other housekeeping constants */ +#define IOBUF_SIZE 4096 + +/* This is what we know about each Huffman coding group */ +struct group_data { + /* We have an extra slot at the end of limit[] for a sentinel value. */ + int limit[MAX_HUFCODE_BITS+1], base[MAX_HUFCODE_BITS], permute[MAX_SYMBOLS]; + int minLen, maxLen; +}; + +/* Structure holding all the housekeeping data, including IO buffers and + * memory that persists between calls to bunzip + * Found the most used member: + * cat this_file.c | sed -e 's/"/ /g' -e "s/'/ /g" | xargs -n1 \ + * | grep 'bd->' | sed 's/^.*bd->/bd->/' | sort | $PAGER + * and moved it (inbufBitCount) to offset 0. + */ +struct bunzip_data { + /* I/O tracking data (file handles, buffers, positions, etc.) */ + unsigned inbufBitCount, inbufBits; + int in_fd, out_fd, inbufCount, inbufPos /*, outbufPos*/; + uint8_t *inbuf /*,*outbuf*/; + + /* State for interrupting output loop */ + int writeCopies, writePos, writeRunCountdown, writeCount; + int writeCurrent; /* actually a uint8_t */ + + /* The CRC values stored in the block header and calculated from the data */ + uint32_t headerCRC, totalCRC, writeCRC; + + /* Intermediate buffer and its size (in bytes) */ + uint32_t *dbuf; + unsigned dbufSize; + + /* For I/O error handling */ + jmp_buf *jmpbuf; + + /* Big things go last (register-relative addressing can be larger for big offsets) */ + uint32_t crc32Table[256]; + uint8_t selectors[32768]; /* nSelectors=15 bits */ + struct group_data groups[MAX_GROUPS]; /* Huffman coding tables */ +}; +/* typedef struct bunzip_data bunzip_data; -- done in .h file */ + + +/* Return the next nnn bits of input. All reads from the compressed input + are done through this function. All reads are big endian */ +static unsigned get_bits(bunzip_data *bd, int bits_wanted) +{ + unsigned bits = 0; + /* Cache bd->inbufBitCount in a CPU register (hopefully): */ + int bit_count = bd->inbufBitCount; + + /* If we need to get more data from the byte buffer, do so. (Loop getting + one byte at a time to enforce endianness and avoid unaligned access.) */ + while (bit_count < bits_wanted) { + + /* If we need to read more data from file into byte buffer, do so */ + if (bd->inbufPos == bd->inbufCount) { + /* if "no input fd" case: in_fd == -1, read fails, we jump */ + bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE); + if (bd->inbufCount <= 0) + longjmp(*bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF); + bd->inbufPos = 0; + } + + /* Avoid 32-bit overflow (dump bit buffer to top of output) */ + if (bit_count >= 24) { + bits = bd->inbufBits & ((1U << bit_count) - 1); + bits_wanted -= bit_count; + bits <<= bits_wanted; + bit_count = 0; + } + + /* Grab next 8 bits of input from buffer. */ + bd->inbufBits = (bd->inbufBits << 8) | bd->inbuf[bd->inbufPos++]; + bit_count += 8; + } + + /* Calculate result */ + bit_count -= bits_wanted; + bd->inbufBitCount = bit_count; + bits |= (bd->inbufBits >> bit_count) & ((1 << bits_wanted) - 1); + + return bits; +} +//#define get_bits(bd, n) (dbg("%d:get_bits()", __LINE__), get_bits(bd, n)) + +/* Unpacks the next block and sets up for the inverse Burrows-Wheeler step. */ +static int get_next_block(bunzip_data *bd) +{ + int groupCount, selector, + i, j, symCount, symTotal, nSelectors, byteCount[256]; + uint8_t uc, symToByte[256], mtfSymbol[256], *selectors; + uint32_t *dbuf; + unsigned origPtr, t; + unsigned dbufCount, runPos; + unsigned runCnt = runCnt; /* for compiler */ + + dbuf = bd->dbuf; + selectors = bd->selectors; + +/* In bbox, we are ok with aborting through setjmp which is set up in start_bunzip */ +#if 0 + /* Reset longjmp I/O error handling */ + i = setjmp(bd->jmpbuf); + if (i) return i; +#endif + + /* Read in header signature and CRC, then validate signature. + (last block signature means CRC is for whole file, return now) */ + i = get_bits(bd, 24); + j = get_bits(bd, 24); + bd->headerCRC = get_bits(bd, 32); + if ((i == 0x177245) && (j == 0x385090)) + return RETVAL_LAST_BLOCK; + if ((i != 0x314159) || (j != 0x265359)) + return RETVAL_NOT_BZIP_DATA; + + /* We can add support for blockRandomised if anybody complains. There was + some code for this in busybox 1.0.0-pre3, but nobody ever noticed that + it didn't actually work. */ + if (get_bits(bd, 1)) + return RETVAL_OBSOLETE_INPUT; + origPtr = get_bits(bd, 24); + if (origPtr > bd->dbufSize) + return RETVAL_DATA_ERROR; + + /* mapping table: if some byte values are never used (encoding things + like ascii text), the compression code removes the gaps to have fewer + symbols to deal with, and writes a sparse bitfield indicating which + values were present. We make a translation table to convert the symbols + back to the corresponding bytes. */ + symTotal = 0; + i = 0; + t = get_bits(bd, 16); + do { + if (t & (1 << 15)) { + unsigned inner_map = get_bits(bd, 16); + do { + if (inner_map & (1 << 15)) + symToByte[symTotal++] = i; + inner_map <<= 1; + i++; + } while (i & 15); + i -= 16; + } + t <<= 1; + i += 16; + } while (i < 256); + + /* How many different Huffman coding groups does this block use? */ + groupCount = get_bits(bd, 3); + if (groupCount < 2 || groupCount > MAX_GROUPS) + return RETVAL_DATA_ERROR; + + /* nSelectors: Every GROUP_SIZE many symbols we select a new Huffman coding + group. Read in the group selector list, which is stored as MTF encoded + bit runs. (MTF=Move To Front, as each value is used it's moved to the + start of the list.) */ + for (i = 0; i < groupCount; i++) + mtfSymbol[i] = i; + nSelectors = get_bits(bd, 15); + if (!nSelectors) + return RETVAL_DATA_ERROR; + for (i = 0; i < nSelectors; i++) { + uint8_t tmp_byte; + /* Get next value */ + int n = 0; + while (get_bits(bd, 1)) { + if (n >= groupCount) + return RETVAL_DATA_ERROR; + n++; + } + /* Decode MTF to get the next selector */ + tmp_byte = mtfSymbol[n]; + while (--n >= 0) + mtfSymbol[n + 1] = mtfSymbol[n]; +//We catch it later, in the second loop where we use selectors[i]. +//Maybe this is a better place, though? +// if (tmp_byte >= groupCount) { +// dbg("%d: selectors[%d]:%d groupCount:%d", +// __LINE__, i, tmp_byte, groupCount); +// return RETVAL_DATA_ERROR; +// } + mtfSymbol[0] = selectors[i] = tmp_byte; + } + + /* Read the Huffman coding tables for each group, which code for symTotal + literal symbols, plus two run symbols (RUNA, RUNB) */ + symCount = symTotal + 2; + for (j = 0; j < groupCount; j++) { + uint8_t length[MAX_SYMBOLS]; + /* 8 bits is ALMOST enough for temp[], see below */ + unsigned temp[MAX_HUFCODE_BITS+1]; + struct group_data *hufGroup; + int *base, *limit; + int minLen, maxLen, pp, len_m1; + + /* Read Huffman code lengths for each symbol. They're stored in + a way similar to mtf; record a starting value for the first symbol, + and an offset from the previous value for every symbol after that. + (Subtracting 1 before the loop and then adding it back at the end is + an optimization that makes the test inside the loop simpler: symbol + length 0 becomes negative, so an unsigned inequality catches it.) */ + len_m1 = get_bits(bd, 5) - 1; + for (i = 0; i < symCount; i++) { + for (;;) { + int two_bits; + if ((unsigned)len_m1 > (MAX_HUFCODE_BITS-1)) + return RETVAL_DATA_ERROR; + + /* If first bit is 0, stop. Else second bit indicates whether + to increment or decrement the value. Optimization: grab 2 + bits and unget the second if the first was 0. */ + two_bits = get_bits(bd, 2); + if (two_bits < 2) { + bd->inbufBitCount++; + break; + } + + /* Add one if second bit 1, else subtract 1. Avoids if/else */ + len_m1 += (((two_bits+1) & 2) - 1); + } + + /* Correct for the initial -1, to get the final symbol length */ + length[i] = len_m1 + 1; + } + + /* Find largest and smallest lengths in this group */ + minLen = maxLen = length[0]; + for (i = 1; i < symCount; i++) { + if (length[i] > maxLen) + maxLen = length[i]; + else if (length[i] < minLen) + minLen = length[i]; + } + + /* Calculate permute[], base[], and limit[] tables from length[]. + * + * permute[] is the lookup table for converting Huffman coded symbols + * into decoded symbols. base[] is the amount to subtract from the + * value of a Huffman symbol of a given length when using permute[]. + * + * limit[] indicates the largest numerical value a symbol with a given + * number of bits can have. This is how the Huffman codes can vary in + * length: each code with a value>limit[length] needs another bit. + */ + hufGroup = bd->groups + j; + hufGroup->minLen = minLen; + hufGroup->maxLen = maxLen; + + /* Note that minLen can't be smaller than 1, so we adjust the base + and limit array pointers so we're not always wasting the first + entry. We do this again when using them (during symbol decoding). */ + base = hufGroup->base - 1; + limit = hufGroup->limit - 1; + + /* Calculate permute[]. Concurrently, initialize temp[] and limit[]. */ + pp = 0; + for (i = minLen; i <= maxLen; i++) { + int k; + temp[i] = limit[i] = 0; + for (k = 0; k < symCount; k++) + if (length[k] == i) + hufGroup->permute[pp++] = k; + } + + /* Count symbols coded for at each bit length */ + /* NB: in pathological cases, temp[8] can end ip being 256. + * That's why uint8_t is too small for temp[]. */ + for (i = 0; i < symCount; i++) + temp[length[i]]++; + + /* Calculate limit[] (the largest symbol-coding value at each bit + * length, which is (previous limit<<1)+symbols at this level), and + * base[] (number of symbols to ignore at each bit length, which is + * limit minus the cumulative count of symbols coded for already). */ + pp = t = 0; + for (i = minLen; i < maxLen;) { + unsigned temp_i = temp[i]; + + pp += temp_i; + + /* We read the largest possible symbol size and then unget bits + after determining how many we need, and those extra bits could + be set to anything. (They're noise from future symbols.) At + each level we're really only interested in the first few bits, + so here we set all the trailing to-be-ignored bits to 1 so they + don't affect the value>limit[length] comparison. */ + limit[i] = (pp << (maxLen - i)) - 1; + pp <<= 1; + t += temp_i; + base[++i] = pp - t; + } + limit[maxLen] = pp + temp[maxLen] - 1; + limit[maxLen+1] = INT_MAX; /* Sentinel value for reading next sym. */ + base[minLen] = 0; + } + + /* We've finished reading and digesting the block header. Now read this + block's Huffman coded symbols from the file and undo the Huffman coding + and run length encoding, saving the result into dbuf[dbufCount++] = uc */ + + /* Initialize symbol occurrence counters and symbol Move To Front table */ + /*memset(byteCount, 0, sizeof(byteCount)); - smaller, but slower */ + for (i = 0; i < 256; i++) { + byteCount[i] = 0; + mtfSymbol[i] = (uint8_t)i; + } + + /* Loop through compressed symbols. */ + + runPos = dbufCount = selector = 0; + for (;;) { + struct group_data *hufGroup; + int *base, *limit; + int nextSym; + uint8_t ngrp; + + /* Fetch next Huffman coding group from list. */ + symCount = GROUP_SIZE - 1; + if (selector >= nSelectors) + return RETVAL_DATA_ERROR; + ngrp = selectors[selector++]; + if (ngrp >= groupCount) { + dbg("%d selectors[%d]:%d groupCount:%d", + __LINE__, selector-1, ngrp, groupCount); + return RETVAL_DATA_ERROR; + } + hufGroup = bd->groups + ngrp; + base = hufGroup->base - 1; + limit = hufGroup->limit - 1; + + continue_this_group: + /* Read next Huffman-coded symbol. */ + + /* Note: It is far cheaper to read maxLen bits and back up than it is + to read minLen bits and then add additional bit at a time, testing + as we go. Because there is a trailing last block (with file CRC), + there is no danger of the overread causing an unexpected EOF for a + valid compressed file. + */ + if (1) { + /* As a further optimization, we do the read inline + (falling back to a call to get_bits if the buffer runs dry). + */ + int new_cnt; + while ((new_cnt = bd->inbufBitCount - hufGroup->maxLen) < 0) { + /* bd->inbufBitCount < hufGroup->maxLen */ + if (bd->inbufPos == bd->inbufCount) { + nextSym = get_bits(bd, hufGroup->maxLen); + goto got_huff_bits; + } + bd->inbufBits = (bd->inbufBits << 8) | bd->inbuf[bd->inbufPos++]; + bd->inbufBitCount += 8; + }; + bd->inbufBitCount = new_cnt; /* "bd->inbufBitCount -= hufGroup->maxLen;" */ + nextSym = (bd->inbufBits >> new_cnt) & ((1 << hufGroup->maxLen) - 1); + got_huff_bits: ; + } else { /* unoptimized equivalent */ + nextSym = get_bits(bd, hufGroup->maxLen); + } + /* Figure how many bits are in next symbol and unget extras */ + i = hufGroup->minLen; + while (nextSym > limit[i]) + ++i; + j = hufGroup->maxLen - i; + if (j < 0) + return RETVAL_DATA_ERROR; + bd->inbufBitCount += j; + + /* Huffman decode value to get nextSym (with bounds checking) */ + nextSym = (nextSym >> j) - base[i]; + if ((unsigned)nextSym >= MAX_SYMBOLS) + return RETVAL_DATA_ERROR; + nextSym = hufGroup->permute[nextSym]; + + /* We have now decoded the symbol, which indicates either a new literal + byte, or a repeated run of the most recent literal byte. First, + check if nextSym indicates a repeated run, and if so loop collecting + how many times to repeat the last literal. */ + if ((unsigned)nextSym <= SYMBOL_RUNB) { /* RUNA or RUNB */ + + /* If this is the start of a new run, zero out counter */ + if (runPos == 0) { + runPos = 1; + runCnt = 0; + } + + /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at + each bit position, add 1 or 2 instead. For example, + 1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2. + You can make any bit pattern that way using 1 less symbol than + the basic or 0/1 method (except all bits 0, which would use no + symbols, but a run of length 0 doesn't mean anything in this + context). Thus space is saved. */ + runCnt += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */ +//The 32-bit overflow of runCnt wasn't yet seen, but probably can happen. +//This would be the fix (catches too large count way before it can overflow): +// if (runCnt > bd->dbufSize) { +// dbg("runCnt:%u > dbufSize:%u RETVAL_DATA_ERROR", +// runCnt, bd->dbufSize); +// return RETVAL_DATA_ERROR; +// } + if (runPos < bd->dbufSize) runPos <<= 1; + goto end_of_huffman_loop; + } + + /* When we hit the first non-run symbol after a run, we now know + how many times to repeat the last literal, so append that many + copies to our buffer of decoded symbols (dbuf) now. (The last + literal used is the one at the head of the mtfSymbol array.) */ + if (runPos != 0) { + uint8_t tmp_byte; + if (dbufCount + runCnt > bd->dbufSize) { + dbg("dbufCount:%u+runCnt:%u %u > dbufSize:%u RETVAL_DATA_ERROR", + dbufCount, runCnt, dbufCount + runCnt, bd->dbufSize); + return RETVAL_DATA_ERROR; + } + tmp_byte = symToByte[mtfSymbol[0]]; + byteCount[tmp_byte] += runCnt; + while ((int)--runCnt >= 0) + dbuf[dbufCount++] = (uint32_t)tmp_byte; + runPos = 0; + } + + /* Is this the terminating symbol? */ + if (nextSym > symTotal) break; + + /* At this point, nextSym indicates a new literal character. Subtract + one to get the position in the MTF array at which this literal is + currently to be found. (Note that the result can't be -1 or 0, + because 0 and 1 are RUNA and RUNB. But another instance of the + first symbol in the mtf array, position 0, would have been handled + as part of a run above. Therefore 1 unused mtf position minus + 2 non-literal nextSym values equals -1.) */ + if (dbufCount >= bd->dbufSize) return RETVAL_DATA_ERROR; + i = nextSym - 1; + uc = mtfSymbol[i]; + + /* Adjust the MTF array. Since we typically expect to move only a + * small number of symbols, and are bound by 256 in any case, using + * memmove here would typically be bigger and slower due to function + * call overhead and other assorted setup costs. */ + do { + mtfSymbol[i] = mtfSymbol[i-1]; + } while (--i); + mtfSymbol[0] = uc; + uc = symToByte[uc]; + + /* We have our literal byte. Save it into dbuf. */ + byteCount[uc]++; + dbuf[dbufCount++] = (uint32_t)uc; + + /* Skip group initialization if we're not done with this group. Done + * this way to avoid compiler warning. */ + end_of_huffman_loop: + if (--symCount >= 0) goto continue_this_group; + } + + /* At this point, we've read all the Huffman-coded symbols (and repeated + runs) for this block from the input stream, and decoded them into the + intermediate buffer. There are dbufCount many decoded bytes in dbuf[]. + Now undo the Burrows-Wheeler transform on dbuf. + See http://dogma.net/markn/articles/bwt/bwt.htm + */ + + /* Turn byteCount into cumulative occurrence counts of 0 to n-1. */ + j = 0; + for (i = 0; i < 256; i++) { + int tmp_count = j + byteCount[i]; + byteCount[i] = j; + j = tmp_count; + } + + /* Figure out what order dbuf would be in if we sorted it. */ + for (i = 0; i < dbufCount; i++) { + uint8_t tmp_byte = (uint8_t)dbuf[i]; + int tmp_count = byteCount[tmp_byte]; + dbuf[tmp_count] |= (i << 8); + byteCount[tmp_byte] = tmp_count + 1; + } + + /* Decode first byte by hand to initialize "previous" byte. Note that it + doesn't get output, and if the first three characters are identical + it doesn't qualify as a run (hence writeRunCountdown=5). */ + if (dbufCount) { + uint32_t tmp; + if ((int)origPtr >= dbufCount) return RETVAL_DATA_ERROR; + tmp = dbuf[origPtr]; + bd->writeCurrent = (uint8_t)tmp; + bd->writePos = (tmp >> 8); + bd->writeRunCountdown = 5; + } + bd->writeCount = dbufCount; + + return RETVAL_OK; +} + +/* Undo Burrows-Wheeler transform on intermediate buffer to produce output. + If start_bunzip was initialized with out_fd=-1, then up to len bytes of + data are written to outbuf. Return value is number of bytes written or + error (all errors are negative numbers). If out_fd!=-1, outbuf and len + are ignored, data is written to out_fd and return is RETVAL_OK or error. + + NB: read_bunzip returns < 0 on error, or the number of *unfilled* bytes + in outbuf. IOW: on EOF returns len ("all bytes are not filled"), not 0. + (Why? This allows to get rid of one local variable) +*/ +int FAST_FUNC read_bunzip(bunzip_data *bd, char *outbuf, int len) +{ + const uint32_t *dbuf; + int pos, current, previous; + uint32_t CRC; + + /* If we already have error/end indicator, return it */ + if (bd->writeCount < 0) + return bd->writeCount; + + dbuf = bd->dbuf; + + /* Register-cached state (hopefully): */ + pos = bd->writePos; + current = bd->writeCurrent; + CRC = bd->writeCRC; /* small loss on x86-32 (not enough regs), win on x86-64 */ + + /* We will always have pending decoded data to write into the output + buffer unless this is the very first call (in which case we haven't + Huffman-decoded a block into the intermediate buffer yet). */ + if (bd->writeCopies) { + + dec_writeCopies: + /* Inside the loop, writeCopies means extra copies (beyond 1) */ + --bd->writeCopies; + + /* Loop outputting bytes */ + for (;;) { + + /* If the output buffer is full, save cached state and return */ + if (--len < 0) { + /* Unlikely branch. + * Use of "goto" instead of keeping code here + * helps compiler to realize this. */ + goto outbuf_full; + } + + /* Write next byte into output buffer, updating CRC */ + *outbuf++ = current; + CRC = (CRC << 8) ^ bd->crc32Table[(CRC >> 24) ^ current]; + + /* Loop now if we're outputting multiple copies of this byte */ + if (bd->writeCopies) { + /* Unlikely branch */ + /*--bd->writeCopies;*/ + /*continue;*/ + /* Same, but (ab)using other existing --writeCopies operation + * (and this if() compiles into just test+branch pair): */ + goto dec_writeCopies; + } + decode_next_byte: + if (--bd->writeCount < 0) + break; /* input block is fully consumed, need next one */ + + /* Follow sequence vector to undo Burrows-Wheeler transform */ + previous = current; + pos = dbuf[pos]; + current = (uint8_t)pos; + pos >>= 8; + + /* After 3 consecutive copies of the same byte, the 4th + * is a repeat count. We count down from 4 instead + * of counting up because testing for non-zero is faster */ + if (--bd->writeRunCountdown != 0) { + if (current != previous) + bd->writeRunCountdown = 4; + } else { + /* Unlikely branch */ + /* We have a repeated run, this byte indicates the count */ + bd->writeCopies = current; + current = previous; + bd->writeRunCountdown = 5; + + /* Sometimes there are just 3 bytes (run length 0) */ + if (!bd->writeCopies) goto decode_next_byte; + + /* Subtract the 1 copy we'd output anyway to get extras */ + --bd->writeCopies; + } + } /* for(;;) */ + + /* Decompression of this input block completed successfully */ + bd->writeCRC = CRC = ~CRC; + bd->totalCRC = ((bd->totalCRC << 1) | (bd->totalCRC >> 31)) ^ CRC; + + /* If this block had a CRC error, force file level CRC error */ + if (CRC != bd->headerCRC) { + bd->totalCRC = bd->headerCRC + 1; + return RETVAL_LAST_BLOCK; + } + } + + /* Refill the intermediate buffer by Huffman-decoding next block of input */ + { + int r = get_next_block(bd); + if (r) { /* error/end */ + bd->writeCount = r; + return (r != RETVAL_LAST_BLOCK) ? r : len; + } + } + + CRC = ~0; + pos = bd->writePos; + current = bd->writeCurrent; + goto decode_next_byte; + + outbuf_full: + /* Output buffer is full, save cached state and return */ + bd->writePos = pos; + bd->writeCurrent = current; + bd->writeCRC = CRC; + + bd->writeCopies++; + + return 0; +} + +/* Allocate the structure, read file header. If in_fd==-1, inbuf must contain + a complete bunzip file (len bytes long). If in_fd!=-1, inbuf and len are + ignored, and data is read from file handle into temporary buffer. */ + +/* Because bunzip2 is used for help text unpacking, and because bb_show_usage() + should work for NOFORK applets too, we must be extremely careful to not leak + any allocations! */ +int FAST_FUNC start_bunzip( + void *jmpbuf, + bunzip_data **bdp, + int in_fd, + const void *inbuf, int len) +{ + bunzip_data *bd; + unsigned i; + enum { + BZh0 = ('B' << 24) + ('Z' << 16) + ('h' << 8) + '0', + h0 = ('h' << 8) + '0', + }; + + /* Figure out how much data to allocate */ + i = sizeof(bunzip_data); + if (in_fd != -1) + i += IOBUF_SIZE; + + /* Allocate bunzip_data. Most fields initialize to zero. */ + bd = *bdp = xzalloc(i); + + bd->jmpbuf = jmpbuf; + + /* Setup input buffer */ + bd->in_fd = in_fd; + if (-1 == in_fd) { + /* in this case, bd->inbuf is read-only */ + bd->inbuf = (void*)inbuf; /* cast away const-ness */ + } else { + bd->inbuf = (uint8_t*)(bd + 1); + memcpy(bd->inbuf, inbuf, len); + } + bd->inbufCount = len; + + /* Init the CRC32 table (big endian) */ + crc32_filltable(bd->crc32Table, 1); + + /* Ensure that file starts with "BZh['1'-'9']." */ + /* Update: now caller verifies 1st two bytes, makes .gz/.bz2 + * integration easier */ + /* was: */ + /* i = get_bits(bd, 32); */ + /* if ((unsigned)(i - BZh0 - 1) >= 9) return RETVAL_NOT_BZIP_DATA; */ + i = get_bits(bd, 16); + if ((unsigned)(i - h0 - 1) >= 9) return RETVAL_NOT_BZIP_DATA; + + /* Fourth byte (ascii '1'-'9') indicates block size in units of 100k of + uncompressed data. Allocate intermediate buffer for block. */ + /* bd->dbufSize = 100000 * (i - BZh0); */ + bd->dbufSize = 100000 * (i - h0); + + /* Cannot use xmalloc - may leak bd in NOFORK case! */ + bd->dbuf = malloc_or_warn(bd->dbufSize * sizeof(bd->dbuf[0])); + if (!bd->dbuf) { + free(bd); + xfunc_die(); + } + return RETVAL_OK; +} + +void FAST_FUNC dealloc_bunzip(bunzip_data *bd) +{ + free(bd->dbuf); + free(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_state_t *xstate) +{ + IF_DESKTOP(long long total_written = 0;) + bunzip_data *bd; + char *outbuf; + int i; + unsigned len; + + if (check_signature16(xstate, BZIP2_MAGIC)) + return -1; + + outbuf = xmalloc(IOBUF_SIZE); + len = 0; + while (1) { /* "Process one BZ... stream" loop */ + jmp_buf jmpbuf; + + /* Setup for I/O error handling via longjmp */ + i = setjmp(jmpbuf); + if (i == 0) + i = start_bunzip(&jmpbuf, &bd, xstate->src_fd, outbuf + 2, len); + + if (i == 0) { + while (1) { /* "Produce some output bytes" loop */ + i = read_bunzip(bd, outbuf, IOBUF_SIZE); + if (i < 0) /* error? */ + break; + i = IOBUF_SIZE - i; /* number of bytes produced */ + if (i == 0) /* EOF? */ + break; + if (i != transformer_write(xstate, outbuf, i)) { + i = RETVAL_SHORT_WRITE; + goto release_mem; + } + IF_DESKTOP(total_written += i;) + } + } + + if (i != RETVAL_LAST_BLOCK + /* Observed case when i == RETVAL_OK: + * "bzcat z.bz2", where "z.bz2" is a bzipped zero-length file + * (to be exact, z.bz2 is exactly these 14 bytes: + * 42 5a 68 39 17 72 45 38 50 90 00 00 00 00). + */ + && i != RETVAL_OK + ) { + bb_error_msg("bunzip error %d", i); + break; + } + if (bd->headerCRC != bd->totalCRC) { + bb_error_msg("CRC error"); + break; + } + + /* Successfully unpacked one BZ stream */ + i = RETVAL_OK; + + /* Do we have "BZ..." after last processed byte? + * pbzip2 (parallelized bzip2) produces such files. + */ + len = bd->inbufCount - bd->inbufPos; + memcpy(outbuf, &bd->inbuf[bd->inbufPos], len); + if (len < 2) { + if (safe_read(xstate->src_fd, outbuf + len, 2 - len) != 2 - len) + break; + len = 2; + } + if (*(uint16_t*)outbuf != BZIP2_MAGIC) /* "BZ"? */ + break; + dealloc_bunzip(bd); + len -= 2; + } + + release_mem: + dealloc_bunzip(bd); + free(outbuf); + + return i ? i : IF_DESKTOP(total_written) + 0; +} + +#ifdef TESTING + +static char *const bunzip_errors[] = { + NULL, "Bad file checksum", "Not bzip data", + "Unexpected input EOF", "Unexpected output EOF", "Data error", + "Out of memory", "Obsolete (pre 0.9.5) bzip format not supported" +}; + +/* Dumb little test thing, decompress stdin to stdout */ +int main(int argc, char **argv) +{ + char c; + + int i = unpack_bz2_stream(0, 1); + if (i < 0) + fprintf(stderr, "%s\n", bunzip_errors[-i]); + else if (read(STDIN_FILENO, &c, 1)) + fprintf(stderr, "Trailing garbage ignored\n"); + return -i; +} +#endif diff --git a/busybox/archival/libarchive/decompress_gunzip.c b/busybox/archival/libarchive/decompress_gunzip.c new file mode 100644 index 00000000000000..7f9046b82b98d8 --- /dev/null +++ b/busybox/archival/libarchive/decompress_gunzip.c @@ -0,0 +1,1285 @@ +/* vi: set sw=4 ts=4: */ +/* + * gunzip implementation for busybox + * + * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly. + * + * Originally adjusted for busybox by Sven Rudolph + * based on gzip sources + * + * Adjusted further by Erik Andersen to support + * files as well as stdin/stdout, and to generally behave itself wrt + * command line handling. + * + * General cleanup to better adhere to the style guide and make use of standard + * busybox functions by Glenn McGrath + * + * read_gz interface + associated hacking by Laurence Anderson + * + * Fixed huft_build() so decoding end-of-block code does not grab more bits + * than necessary (this is required by unzip applet), added inflate_cleanup() + * to free leaked bytebuffer memory (used in unzip.c), and some minor style + * guide cleanups by Ed Clark + * + * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface + * Copyright (C) 1992-1993 Jean-loup Gailly + * The unzip code was written and put in the public domain by Mark Adler. + * Portions of the lzw code are derived from the public domain 'compress' + * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, + * Ken Turkowski, Dave Mack and Peter Jannesen. + * + * See the file algorithm.doc for the compression algorithms and file formats. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "bb_archive.h" + +typedef struct huft_t { + unsigned char e; /* number of extra bits or operation */ + unsigned char b; /* number of bits in this code or subcode */ + union { + unsigned short n; /* literal, length base, or distance base */ + struct huft_t *t; /* pointer to next level of table */ + } v; +} huft_t; + +enum { + /* gunzip_window size--must be a power of two, and + * at least 32K for zip's deflate method */ + GUNZIP_WSIZE = 0x8000, + /* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ + BMAX = 16, /* maximum bit length of any code (16 for explode) */ + N_MAX = 288, /* maximum number of codes in any set */ +}; + + +/* This is somewhat complex-looking arrangement, but it allows + * to place decompressor state either in bss or in + * malloc'ed space simply by changing #defines below. + * Sizes on i386: + * text data bss dec hex + * 5256 0 108 5364 14f4 - bss + * 4915 0 0 4915 1333 - malloc + */ +#define STATE_IN_BSS 0 +#define STATE_IN_MALLOC 1 + + +typedef struct state_t { + off_t gunzip_bytes_out; /* number of output bytes */ + uint32_t gunzip_crc; + + int gunzip_src_fd; + unsigned gunzip_outbuf_count; /* bytes in output buffer */ + + unsigned char *gunzip_window; + + uint32_t *gunzip_crc_table; + + /* bitbuffer */ + unsigned gunzip_bb; /* bit buffer */ + unsigned char gunzip_bk; /* bits in bit buffer */ + + /* input (compressed) data */ + unsigned char *bytebuffer; /* buffer itself */ + off_t to_read; /* compressed bytes to read (unzip only, -1 for gunzip) */ +// unsigned bytebuffer_max; /* buffer size */ + unsigned bytebuffer_offset; /* buffer position */ + unsigned bytebuffer_size; /* how much data is there (size <= max) */ + + /* private data of inflate_codes() */ + unsigned inflate_codes_ml; /* masks for bl and bd bits */ + unsigned inflate_codes_md; /* masks for bl and bd bits */ + unsigned inflate_codes_bb; /* bit buffer */ + unsigned inflate_codes_k; /* number of bits in bit buffer */ + unsigned inflate_codes_w; /* current gunzip_window position */ + huft_t *inflate_codes_tl; + huft_t *inflate_codes_td; + unsigned inflate_codes_bl; + unsigned inflate_codes_bd; + unsigned inflate_codes_nn; /* length and index for copy */ + unsigned inflate_codes_dd; + + smallint resume_copy; + + /* private data of inflate_get_next_window() */ + smallint method; /* method == -1 for stored, -2 for codes */ + smallint need_another_block; + smallint end_reached; + + /* private data of inflate_stored() */ + unsigned inflate_stored_n; + unsigned inflate_stored_b; + unsigned inflate_stored_k; + unsigned inflate_stored_w; + + const char *error_msg; + jmp_buf error_jmp; +} state_t; +#define gunzip_bytes_out (S()gunzip_bytes_out ) +#define gunzip_crc (S()gunzip_crc ) +#define gunzip_src_fd (S()gunzip_src_fd ) +#define gunzip_outbuf_count (S()gunzip_outbuf_count) +#define gunzip_window (S()gunzip_window ) +#define gunzip_crc_table (S()gunzip_crc_table ) +#define gunzip_bb (S()gunzip_bb ) +#define gunzip_bk (S()gunzip_bk ) +#define to_read (S()to_read ) +// #define bytebuffer_max (S()bytebuffer_max ) +// Both gunzip and unzip can use constant buffer size now (16k): +#define bytebuffer_max 0x4000 +#define bytebuffer (S()bytebuffer ) +#define bytebuffer_offset (S()bytebuffer_offset ) +#define bytebuffer_size (S()bytebuffer_size ) +#define inflate_codes_ml (S()inflate_codes_ml ) +#define inflate_codes_md (S()inflate_codes_md ) +#define inflate_codes_bb (S()inflate_codes_bb ) +#define inflate_codes_k (S()inflate_codes_k ) +#define inflate_codes_w (S()inflate_codes_w ) +#define inflate_codes_tl (S()inflate_codes_tl ) +#define inflate_codes_td (S()inflate_codes_td ) +#define inflate_codes_bl (S()inflate_codes_bl ) +#define inflate_codes_bd (S()inflate_codes_bd ) +#define inflate_codes_nn (S()inflate_codes_nn ) +#define inflate_codes_dd (S()inflate_codes_dd ) +#define resume_copy (S()resume_copy ) +#define method (S()method ) +#define need_another_block (S()need_another_block ) +#define end_reached (S()end_reached ) +#define inflate_stored_n (S()inflate_stored_n ) +#define inflate_stored_b (S()inflate_stored_b ) +#define inflate_stored_k (S()inflate_stored_k ) +#define inflate_stored_w (S()inflate_stored_w ) +#define error_msg (S()error_msg ) +#define error_jmp (S()error_jmp ) + +/* This is a generic part */ +#if STATE_IN_BSS /* Use global data segment */ +#define DECLARE_STATE /*nothing*/ +#define ALLOC_STATE /*nothing*/ +#define DEALLOC_STATE ((void)0) +#define S() state. +#define PASS_STATE /*nothing*/ +#define PASS_STATE_ONLY /*nothing*/ +#define STATE_PARAM /*nothing*/ +#define STATE_PARAM_ONLY void +static state_t state; +#endif + +#if STATE_IN_MALLOC /* Use malloc space */ +#define DECLARE_STATE state_t *state +#define ALLOC_STATE (state = xzalloc(sizeof(*state))) +#define DEALLOC_STATE free(state) +#define S() state-> +#define PASS_STATE state, +#define PASS_STATE_ONLY state +#define STATE_PARAM state_t *state, +#define STATE_PARAM_ONLY state_t *state +#endif + + +static const uint16_t mask_bits[] ALIGN2 = { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +/* Copy lengths for literal codes 257..285 */ +static const uint16_t cplens[] ALIGN2 = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 +}; + +/* note: see note #13 above about the 258 in this list. */ +/* Extra bits for literal codes 257..285 */ +static const uint8_t cplext[] ALIGN1 = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, + 5, 5, 5, 0, 99, 99 +}; /* 99 == invalid */ + +/* Copy offsets for distance codes 0..29 */ +static const uint16_t cpdist[] ALIGN2 = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 +}; + +/* Extra bits for distance codes */ +static const uint8_t cpdext[] ALIGN1 = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +}; + +/* Tables for deflate from PKZIP's appnote.txt. */ +/* Order of the bit length code lengths */ +static const uint8_t border[] ALIGN1 = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + + +/* + * Free the malloc'ed tables built by huft_build(), which makes a linked + * list of the tables it made, with the links in a dummy first entry of + * each table. + * t: table to free + */ +static void huft_free(huft_t *p) +{ + huft_t *q; + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + while (p) { + q = (--p)->v.t; + free(p); + p = q; + } +} + +static void huft_free_all(STATE_PARAM_ONLY) +{ + huft_free(inflate_codes_tl); + huft_free(inflate_codes_td); + inflate_codes_tl = NULL; + inflate_codes_td = NULL; +} + +static void abort_unzip(STATE_PARAM_ONLY) NORETURN; +static void abort_unzip(STATE_PARAM_ONLY) +{ + huft_free_all(PASS_STATE_ONLY); + longjmp(error_jmp, 1); +} + +static unsigned fill_bitbuffer(STATE_PARAM unsigned bitbuffer, unsigned *current, const unsigned required) +{ + while (*current < required) { + if (bytebuffer_offset >= bytebuffer_size) { + unsigned sz = bytebuffer_max - 4; + if (to_read >= 0 && to_read < sz) /* unzip only */ + sz = to_read; + /* Leave the first 4 bytes empty so we can always unwind the bitbuffer + * to the front of the bytebuffer */ + bytebuffer_size = safe_read(gunzip_src_fd, &bytebuffer[4], sz); + if ((int)bytebuffer_size < 1) { + error_msg = "unexpected end of file"; + abort_unzip(PASS_STATE_ONLY); + } + if (to_read >= 0) /* unzip only */ + to_read -= bytebuffer_size; + bytebuffer_size += 4; + bytebuffer_offset = 4; + } + bitbuffer |= ((unsigned) bytebuffer[bytebuffer_offset]) << *current; + bytebuffer_offset++; + *current += 8; + } + return bitbuffer; +} + + +/* Given a list of code lengths and a maximum table size, make a set of + * tables to decode that set of codes. Return zero on success, one if + * the given code set is incomplete (the tables are still built in this + * case), two if the input is invalid (an oversubscribed set of lengths) + * - in this case stores NULL in *t. + * + * b: code lengths in bits (all assumed <= BMAX) + * n: number of codes (assumed <= N_MAX) + * s: number of simple-valued codes (0..s-1) + * d: list of base values for non-simple codes + * e: list of extra bits for non-simple codes + * t: result: starting table + * m: maximum lookup bits, returns actual + */ +static int huft_build(const unsigned *b, const unsigned n, + const unsigned s, const unsigned short *d, + const unsigned char *e, huft_t **t, unsigned *m) +{ + unsigned a; /* counter for codes of length k */ + unsigned c[BMAX + 1]; /* bit length count table */ + unsigned eob_len; /* length of end-of-block code (value 256) */ + unsigned f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int htl; /* table level */ + unsigned i; /* counter, current code */ + unsigned j; /* counter */ + int k; /* number of bits in current code */ + 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 + 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 */ + unsigned *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + unsigned z; /* number of entries in current table */ + + /* Length of EOB code, if any */ + eob_len = n > 256 ? b[256] : BMAX; + + *t = NULL; + + /* Generate counts for each bit length */ + memset(c, 0, sizeof(c)); + p = b; + i = n; + do { + c[*p]++; /* assume all entries <= BMAX */ + p++; /* can't combine with above line (Solaris bug) */ + } while (--i); + if (c[0] == n) { /* null input - all zero length codes */ + q = xzalloc(3 * sizeof(*q)); + //q[0].v.t = NULL; + q[1].e = 99; /* invalid code marker */ + q[1].b = 1; + q[2].e = 99; /* invalid code marker */ + q[2].b = 1; + *t = q + 1; + *m = 1; + return 0; + } + + /* Find minimum and maximum length, bound *m by those */ + for (j = 1; (j <= BMAX) && (c[j] == 0); j++) + continue; + k = j; /* minimum code length */ + for (i = BMAX; (c[i] == 0) && i; i--) + continue; + g = i; /* maximum code length */ + *m = (*m < j) ? j : ((*m > i) ? i : *m); + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) { + y -= c[j]; + if (y < 0) + return 2; /* bad input: more codes than bits */ + } + y -= c[i]; + if (y < 0) + return 2; + c[i] += y; + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; + xp = x + 2; + while (--i) { /* note that i == g from above */ + j += *p++; + *xp++ = j; + } + + /* 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++; + if (j != 0) { + v[x[j]++] = i; + } + } while (++i < n); + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + htl = -1; /* no tables yet--level -1 */ + w = ws[0] = 0; /* bits decoded */ + u[0] = NULL; /* just to keep compilers happy */ + q = NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) { + a = c[k]; + while (a--) { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > ws[htl + 1]) { + w = ws[++htl]; + + /* compute minimum size table less than or equal to *m bits */ + z = g - w; + z = z > *m ? *m : z; /* upper limit on table size */ + j = k - w; + f = 1 << j; + if (f > a + 1) { /* try a k-w bit table */ + /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + while (++j < z) { /* try smaller tables up to z bits */ + f <<= 1; + if (f <= *++xp) { + break; /* enough codes to use up j bits */ + } + f -= *xp; /* else deduct codes from patterns */ + } + } + j = (w + j > eob_len && w < eob_len) ? eob_len - w : j; /* make EOB code end at table */ + z = 1 << j; /* table entries for j-bit table */ + ws[htl+1] = w + j; /* set bits decoded in stack */ + + /* allocate and link in new table */ + q = xzalloc((z + 1) * sizeof(huft_t)); + *t = q + 1; /* link to list for huft_free() */ + t = &(q->v.t); + u[htl] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (htl) { + x[htl] = i; /* save pattern for backing up */ + r.b = (unsigned char) (w - ws[htl - 1]); /* bits to dump before this table */ + r.e = (unsigned char) (16 + j); /* bits in this table */ + r.v.t = q; /* pointer to this table */ + j = (i & ((1 << w) - 1)) >> ws[htl - 1]; + u[htl - 1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.b = (unsigned char) (k - w); + 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 */ + r.v.n = (unsigned short) (*p++); /* simple code is just the value */ + } else { + r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists */ + r.v.n = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) { + q[j] = r; + } + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) { + i ^= j; + } + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[htl]) { + w = ws[--htl]; + } + } + } + + /* return actual size of base table */ + *m = ws[1]; + + /* Return 1 if we were given an incomplete table */ + return y != 0 && g != 1; +} + + +/* + * inflate (decompress) the codes in a deflated (compressed) block. + * Return an error code or zero if it all goes ok. + * + * tl, td: literal/length and distance decoder tables + * bl, bd: number of bits decoded by tl[] and td[] + */ +/* called once from inflate_block */ + +/* map formerly local static variables to globals */ +#define ml inflate_codes_ml +#define md inflate_codes_md +#define bb inflate_codes_bb +#define k inflate_codes_k +#define w inflate_codes_w +#define tl inflate_codes_tl +#define td inflate_codes_td +#define bl inflate_codes_bl +#define bd inflate_codes_bd +#define nn inflate_codes_nn +#define dd inflate_codes_dd +static void inflate_codes_setup(STATE_PARAM unsigned my_bl, unsigned my_bd) +{ + bl = my_bl; + bd = my_bd; + /* make local copies of globals */ + bb = gunzip_bb; /* initialize bit buffer */ + k = gunzip_bk; + w = gunzip_outbuf_count; /* initialize gunzip_window position */ + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; +} +/* called once from inflate_get_next_window */ +static NOINLINE int inflate_codes(STATE_PARAM_ONLY) +{ + unsigned e; /* table entry flag/number of extra bits */ + huft_t *t; /* pointer to table entry */ + + if (resume_copy) + goto do_copy; + + while (1) { /* do until end of block */ + bb = fill_bitbuffer(PASS_STATE bb, &k, bl); + t = tl + ((unsigned) bb & ml); + e = t->e; + if (e > 16) + do { + if (e == 99) { + abort_unzip(PASS_STATE_ONLY); + } + bb >>= t->b; + k -= t->b; + e -= 16; + bb = fill_bitbuffer(PASS_STATE bb, &k, e); + t = t->v.t + ((unsigned) bb & mask_bits[e]); + e = t->e; + } while (e > 16); + bb >>= t->b; + k -= t->b; + if (e == 16) { /* then it's a literal */ + gunzip_window[w++] = (unsigned char) t->v.n; + if (w == GUNZIP_WSIZE) { + gunzip_outbuf_count = w; + //flush_gunzip_window(); + w = 0; + return 1; // We have a block to read + } + } else { /* it's an EOB or a length */ + /* exit if end of block */ + if (e == 15) { + break; + } + + /* get length of block to copy */ + bb = fill_bitbuffer(PASS_STATE bb, &k, e); + nn = t->v.n + ((unsigned) bb & mask_bits[e]); + bb >>= e; + k -= e; + + /* decode distance of block to copy */ + bb = fill_bitbuffer(PASS_STATE bb, &k, bd); + t = td + ((unsigned) bb & md); + e = t->e; + if (e > 16) + do { + if (e == 99) { + abort_unzip(PASS_STATE_ONLY); + } + bb >>= t->b; + k -= t->b; + e -= 16; + bb = fill_bitbuffer(PASS_STATE bb, &k, e); + t = t->v.t + ((unsigned) bb & mask_bits[e]); + e = t->e; + } while (e > 16); + bb >>= t->b; + k -= t->b; + bb = fill_bitbuffer(PASS_STATE bb, &k, e); + dd = w - t->v.n - ((unsigned) bb & mask_bits[e]); + bb >>= e; + k -= e; + + /* do the copy */ + do_copy: + do { + /* Was: nn -= (e = (e = GUNZIP_WSIZE - ((dd &= GUNZIP_WSIZE - 1) > w ? dd : w)) > nn ? nn : e); */ + /* Who wrote THAT?? rewritten as: */ + unsigned delta; + + dd &= GUNZIP_WSIZE - 1; + e = GUNZIP_WSIZE - (dd > w ? dd : w); + delta = w > dd ? w - dd : dd - w; + if (e > nn) e = nn; + nn -= e; + + /* copy to new buffer to prevent possible overwrite */ + if (delta >= e) { + memcpy(gunzip_window + w, gunzip_window + dd, e); + w += e; + dd += e; + } else { + /* do it slow to avoid memcpy() overlap */ + /* !NOMEMCPY */ + do { + gunzip_window[w++] = gunzip_window[dd++]; + } while (--e); + } + if (w == GUNZIP_WSIZE) { + gunzip_outbuf_count = w; + resume_copy = (nn != 0); + //flush_gunzip_window(); + w = 0; + return 1; + } + } while (nn); + resume_copy = 0; + } + } + + /* restore the globals from the locals */ + gunzip_outbuf_count = w; /* restore global gunzip_window pointer */ + gunzip_bb = bb; /* restore global bit buffer */ + gunzip_bk = k; + + /* normally just after call to inflate_codes, but save code by putting it here */ + /* free the decoding tables (tl and td), return */ + huft_free_all(PASS_STATE_ONLY); + + /* done */ + return 0; +} +#undef ml +#undef md +#undef bb +#undef k +#undef w +#undef tl +#undef td +#undef bl +#undef bd +#undef nn +#undef dd + + +/* called once from inflate_block */ +static void inflate_stored_setup(STATE_PARAM int my_n, int my_b, int my_k) +{ + inflate_stored_n = my_n; + inflate_stored_b = my_b; + inflate_stored_k = my_k; + /* initialize gunzip_window position */ + inflate_stored_w = gunzip_outbuf_count; +} +/* called once from inflate_get_next_window */ +static int inflate_stored(STATE_PARAM_ONLY) +{ + /* read and output the compressed data */ + while (inflate_stored_n--) { + inflate_stored_b = fill_bitbuffer(PASS_STATE inflate_stored_b, &inflate_stored_k, 8); + gunzip_window[inflate_stored_w++] = (unsigned char) inflate_stored_b; + if (inflate_stored_w == GUNZIP_WSIZE) { + gunzip_outbuf_count = inflate_stored_w; + //flush_gunzip_window(); + inflate_stored_w = 0; + inflate_stored_b >>= 8; + inflate_stored_k -= 8; + return 1; /* We have a block */ + } + inflate_stored_b >>= 8; + inflate_stored_k -= 8; + } + + /* restore the globals from the locals */ + gunzip_outbuf_count = inflate_stored_w; /* restore global gunzip_window pointer */ + gunzip_bb = inflate_stored_b; /* restore global bit buffer */ + gunzip_bk = inflate_stored_k; + return 0; /* Finished */ +} + + +/* + * decompress an inflated block + * e: last block flag + * + * GLOBAL VARIABLES: bb, kk, + */ +/* Return values: -1 = inflate_stored, -2 = inflate_codes */ +/* One callsite in inflate_get_next_window */ +static int inflate_block(STATE_PARAM smallint *e) +{ + unsigned ll[286 + 30]; /* literal/length and distance code lengths */ + unsigned t; /* block type */ + unsigned b; /* bit buffer */ + unsigned k; /* number of bits in bit buffer */ + + /* make local bit buffer */ + + b = gunzip_bb; + k = gunzip_bk; + + /* read in last block bit */ + b = fill_bitbuffer(PASS_STATE b, &k, 1); + *e = b & 1; + b >>= 1; + k -= 1; + + /* read in block type */ + b = fill_bitbuffer(PASS_STATE b, &k, 2); + t = (unsigned) b & 3; + b >>= 2; + k -= 2; + + /* restore the global bit buffer */ + gunzip_bb = b; + gunzip_bk = k; + + /* Do we see block type 1 often? Yes! + * TODO: fix performance problem (see below) */ + //bb_error_msg("blktype %d", t); + + /* inflate that block type */ + switch (t) { + case 0: /* Inflate stored */ + { + unsigned n; /* number of bytes in block */ + unsigned b_stored; /* bit buffer */ + unsigned k_stored; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b_stored = gunzip_bb; /* initialize bit buffer */ + k_stored = gunzip_bk; + + /* go to byte boundary */ + n = k_stored & 7; + b_stored >>= n; + k_stored -= n; + + /* get the length and its complement */ + b_stored = fill_bitbuffer(PASS_STATE b_stored, &k_stored, 16); + n = ((unsigned) b_stored & 0xffff); + b_stored >>= 16; + k_stored -= 16; + + b_stored = fill_bitbuffer(PASS_STATE b_stored, &k_stored, 16); + if (n != (unsigned) ((~b_stored) & 0xffff)) { + abort_unzip(PASS_STATE_ONLY); /* error in compressed data */ + } + b_stored >>= 16; + k_stored -= 16; + + inflate_stored_setup(PASS_STATE n, b_stored, k_stored); + + return -1; + } + case 1: + /* Inflate fixed + * decompress an inflated type 1 (fixed Huffman codes) block. We should + * either replace this with a custom decoder, or at least precompute the + * Huffman tables. TODO */ + { + int i; /* temporary variable */ + unsigned bl; /* lookup bits for tl */ + unsigned bd; /* lookup bits for td */ + /* gcc 4.2.1 is too dumb to reuse stackspace. Moved up... */ + //unsigned ll[288]; /* length list for huft_build */ + + /* set up literal table */ + for (i = 0; i < 144; i++) + ll[i] = 8; + for (; i < 256; i++) + ll[i] = 9; + for (; i < 280; i++) + ll[i] = 7; + for (; i < 288; i++) /* make a complete, but wrong code set */ + ll[i] = 8; + bl = 7; + huft_build(ll, 288, 257, cplens, cplext, &inflate_codes_tl, &bl); + /* huft_build() never return nonzero - we use known data */ + + /* set up distance table */ + for (i = 0; i < 30; i++) /* make an incomplete code set */ + ll[i] = 5; + bd = 5; + huft_build(ll, 30, 0, cpdist, cpdext, &inflate_codes_td, &bd); + + /* set up data for inflate_codes() */ + inflate_codes_setup(PASS_STATE bl, bd); + + /* huft_free code moved into inflate_codes */ + + return -2; + } + case 2: /* Inflate dynamic */ + { + enum { dbits = 6 }; /* bits in base distance lookup table */ + enum { lbits = 9 }; /* bits in base literal/length lookup table */ + + huft_t *td; /* distance code table */ + unsigned i; /* temporary variables */ + unsigned j; + unsigned l; /* last length */ + unsigned m; /* mask for bit lengths table */ + unsigned n; /* number of lengths to get */ + unsigned bl; /* lookup bits for tl */ + unsigned bd; /* lookup bits for td */ + unsigned nb; /* number of bit length codes */ + unsigned nl; /* number of literal/length codes */ + unsigned nd; /* number of distance codes */ + + //unsigned ll[286 + 30];/* literal/length and distance code lengths */ + unsigned b_dynamic; /* bit buffer */ + unsigned k_dynamic; /* number of bits in bit buffer */ + + /* make local bit buffer */ + b_dynamic = gunzip_bb; + k_dynamic = gunzip_bk; + + /* read in table lengths */ + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 5); + nl = 257 + ((unsigned) b_dynamic & 0x1f); /* number of literal/length codes */ + + b_dynamic >>= 5; + k_dynamic -= 5; + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 5); + nd = 1 + ((unsigned) b_dynamic & 0x1f); /* number of distance codes */ + + b_dynamic >>= 5; + k_dynamic -= 5; + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 4); + nb = 4 + ((unsigned) b_dynamic & 0xf); /* number of bit length codes */ + + b_dynamic >>= 4; + k_dynamic -= 4; + if (nl > 286 || nd > 30) { + abort_unzip(PASS_STATE_ONLY); /* bad lengths */ + } + + /* read in bit-length-code lengths */ + for (j = 0; j < nb; j++) { + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 3); + ll[border[j]] = (unsigned) b_dynamic & 7; + b_dynamic >>= 3; + k_dynamic -= 3; + } + for (; j < 19; j++) + ll[border[j]] = 0; + + /* build decoding table for trees - single level, 7 bit lookup */ + bl = 7; + i = huft_build(ll, 19, 19, NULL, NULL, &inflate_codes_tl, &bl); + if (i != 0) { + abort_unzip(PASS_STATE_ONLY); //return i; /* incomplete code set */ + } + + /* read in literal and distance code lengths */ + n = nl + nd; + m = mask_bits[bl]; + i = l = 0; + while ((unsigned) i < n) { + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, (unsigned)bl); + td = inflate_codes_tl + ((unsigned) b_dynamic & m); + j = td->b; + b_dynamic >>= j; + k_dynamic -= j; + j = td->v.n; + if (j < 16) { /* length of code in bits (0..15) */ + ll[i++] = l = j; /* save last length in l */ + } else if (j == 16) { /* repeat last length 3 to 6 times */ + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 2); + j = 3 + ((unsigned) b_dynamic & 3); + b_dynamic >>= 2; + k_dynamic -= 2; + if ((unsigned) i + j > n) { + abort_unzip(PASS_STATE_ONLY); //return 1; + } + while (j--) { + ll[i++] = l; + } + } else if (j == 17) { /* 3 to 10 zero length codes */ + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 3); + j = 3 + ((unsigned) b_dynamic & 7); + b_dynamic >>= 3; + k_dynamic -= 3; + if ((unsigned) i + j > n) { + abort_unzip(PASS_STATE_ONLY); //return 1; + } + while (j--) { + ll[i++] = 0; + } + l = 0; + } else { /* j == 18: 11 to 138 zero length codes */ + b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 7); + j = 11 + ((unsigned) b_dynamic & 0x7f); + b_dynamic >>= 7; + k_dynamic -= 7; + if ((unsigned) i + j > n) { + abort_unzip(PASS_STATE_ONLY); //return 1; + } + while (j--) { + ll[i++] = 0; + } + l = 0; + } + } + + /* free decoding table for trees */ + huft_free(inflate_codes_tl); + + /* restore the global bit buffer */ + gunzip_bb = b_dynamic; + gunzip_bk = k_dynamic; + + /* build the decoding tables for literal/length and distance codes */ + bl = lbits; + + i = huft_build(ll, nl, 257, cplens, cplext, &inflate_codes_tl, &bl); + 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) { + abort_unzip(PASS_STATE_ONLY); + } + + /* set up data for inflate_codes() */ + inflate_codes_setup(PASS_STATE bl, bd); + + /* huft_free code moved into inflate_codes */ + + return -2; + } + default: + abort_unzip(PASS_STATE_ONLY); + } +} + +/* Two callsites, both in inflate_get_next_window */ +static void calculate_gunzip_crc(STATE_PARAM_ONLY) +{ + gunzip_crc = crc32_block_endian0(gunzip_crc, gunzip_window, gunzip_outbuf_count, gunzip_crc_table); + gunzip_bytes_out += gunzip_outbuf_count; +} + +/* One callsite in inflate_unzip_internal */ +static int inflate_get_next_window(STATE_PARAM_ONLY) +{ + gunzip_outbuf_count = 0; + + while (1) { + int ret; + + if (need_another_block) { + if (end_reached) { + calculate_gunzip_crc(PASS_STATE_ONLY); + end_reached = 0; + /* NB: need_another_block is still set */ + return 0; /* Last block */ + } + method = inflate_block(PASS_STATE &end_reached); + need_another_block = 0; + } + + switch (method) { + case -1: + ret = inflate_stored(PASS_STATE_ONLY); + break; + case -2: + ret = inflate_codes(PASS_STATE_ONLY); + break; + default: /* cannot happen */ + abort_unzip(PASS_STATE_ONLY); + } + + if (ret == 1) { + calculate_gunzip_crc(PASS_STATE_ONLY); + return 1; /* more data left */ + } + need_another_block = 1; /* end of that block */ + } + /* Doesnt get here */ +} + + +/* Called from unpack_gz_stream() and inflate_unzip() */ +static IF_DESKTOP(long long) int +inflate_unzip_internal(STATE_PARAM transformer_state_t *xstate) +{ + IF_DESKTOP(long long) int n = 0; + ssize_t nwrote; + + /* Allocate all global buffers (for DYN_ALLOC option) */ + gunzip_window = xmalloc(GUNZIP_WSIZE); + gunzip_outbuf_count = 0; + gunzip_bytes_out = 0; + gunzip_src_fd = xstate->src_fd; + + /* (re) initialize state */ + method = -1; + need_another_block = 1; + resume_copy = 0; + gunzip_bk = 0; + gunzip_bb = 0; + + /* Create the crc table */ + gunzip_crc_table = crc32_new_table_le(); + gunzip_crc = ~0; + + error_msg = "corrupted data"; + if (setjmp(error_jmp)) { + /* Error from deep inside zip machinery */ + bb_error_msg(error_msg); + n = -1; + goto ret; + } + + while (1) { + int r = inflate_get_next_window(PASS_STATE_ONLY); + nwrote = transformer_write(xstate, gunzip_window, gunzip_outbuf_count); + if (nwrote == (ssize_t)-1) { + n = -1; + goto ret; + } + IF_DESKTOP(n += nwrote;) + if (r == 0) break; + } + + /* Store unused bytes in a global buffer so calling applets can access it */ + if (gunzip_bk >= 8) { + /* Undo too much lookahead. The next read will be byte aligned + * so we can discard unused bits in the last meaningful byte. */ + bytebuffer_offset--; + bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff; + gunzip_bb >>= 8; + gunzip_bk -= 8; + } + ret: + /* Cleanup */ + free(gunzip_window); + free(gunzip_crc_table); + return n; +} + + +/* External entry points */ + +/* For unzip */ + +IF_DESKTOP(long long) int FAST_FUNC +inflate_unzip(transformer_state_t *xstate) +{ + IF_DESKTOP(long long) int n; + DECLARE_STATE; + + ALLOC_STATE; + + to_read = xstate->bytes_in; +// bytebuffer_max = 0x8000; + bytebuffer_offset = 4; + bytebuffer = xmalloc(bytebuffer_max); + n = inflate_unzip_internal(PASS_STATE xstate); + free(bytebuffer); + + xstate->crc32 = gunzip_crc; + xstate->bytes_out = gunzip_bytes_out; + DEALLOC_STATE; + return n; +} + + +/* For gunzip */ + +/* helpers first */ + +/* Top up the input buffer with at least n bytes. */ +static int top_up(STATE_PARAM unsigned n) +{ + int count = bytebuffer_size - bytebuffer_offset; + + if (count < (int)n) { + memmove(bytebuffer, &bytebuffer[bytebuffer_offset], count); + bytebuffer_offset = 0; + bytebuffer_size = full_read(gunzip_src_fd, &bytebuffer[count], bytebuffer_max - count); + if ((int)bytebuffer_size < 0) { + bb_error_msg(bb_msg_read_error); + return 0; + } + bytebuffer_size += count; + if (bytebuffer_size < n) + return 0; + } + return 1; +} + +static uint16_t buffer_read_le_u16(STATE_PARAM_ONLY) +{ + uint16_t res; +#if BB_LITTLE_ENDIAN + move_from_unaligned16(res, &bytebuffer[bytebuffer_offset]); +#else + res = bytebuffer[bytebuffer_offset]; + res |= bytebuffer[bytebuffer_offset + 1] << 8; +#endif + bytebuffer_offset += 2; + return res; +} + +static uint32_t buffer_read_le_u32(STATE_PARAM_ONLY) +{ + uint32_t res; +#if BB_LITTLE_ENDIAN + move_from_unaligned32(res, &bytebuffer[bytebuffer_offset]); +#else + res = bytebuffer[bytebuffer_offset]; + res |= bytebuffer[bytebuffer_offset + 1] << 8; + res |= bytebuffer[bytebuffer_offset + 2] << 16; + res |= bytebuffer[bytebuffer_offset + 3] << 24; +#endif + bytebuffer_offset += 4; + return res; +} + +static int check_header_gzip(STATE_PARAM transformer_state_t *xstate) +{ + union { + unsigned char raw[8]; + struct { + uint8_t gz_method; + uint8_t flags; + uint32_t mtime; + uint8_t xtra_flags_UNUSED; + uint8_t os_flags_UNUSED; + } PACKED formatted; + } header; + + BUILD_BUG_ON(sizeof(header) != 8); + + /* + * Rewind bytebuffer. We use the beginning because the header has 8 + * bytes, leaving enough for unwinding afterwards. + */ + bytebuffer_size -= bytebuffer_offset; + memmove(bytebuffer, &bytebuffer[bytebuffer_offset], bytebuffer_size); + bytebuffer_offset = 0; + + if (!top_up(PASS_STATE 8)) + return 0; + memcpy(header.raw, &bytebuffer[bytebuffer_offset], 8); + bytebuffer_offset += 8; + + /* Check the compression method */ + if (header.formatted.gz_method != 8) { + return 0; + } + + if (header.formatted.flags & 0x04) { + /* bit 2 set: extra field present */ + unsigned extra_short; + + if (!top_up(PASS_STATE 2)) + return 0; + extra_short = buffer_read_le_u16(PASS_STATE_ONLY); + if (!top_up(PASS_STATE extra_short)) + return 0; + /* Ignore extra field */ + bytebuffer_offset += extra_short; + } + + /* Discard original name and file comment if any */ + /* bit 3 set: original file name present */ + /* bit 4 set: file comment present */ + if (header.formatted.flags & 0x18) { + while (1) { + do { + if (!top_up(PASS_STATE 1)) + return 0; + } while (bytebuffer[bytebuffer_offset++] != 0); + if ((header.formatted.flags & 0x18) != 0x18) + break; + header.formatted.flags &= ~0x18; + } + } + + xstate->mtime = SWAP_LE32(header.formatted.mtime); + + /* Read the header checksum */ + if (header.formatted.flags & 0x02) { + if (!top_up(PASS_STATE 2)) + return 0; + bytebuffer_offset += 2; + } + return 1; +} + +IF_DESKTOP(long long) int FAST_FUNC +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(xstate, GZIP_MAGIC)) + return -1; +#else + if (!xstate->signature_skipped) { + uint16_t magic2; + + if (full_read(xstate->src_fd, &magic2, 2) != 2) { + bad_magic: + bb_error_msg("invalid magic"); + return -1; + } + if (magic2 == COMPRESS_MAGIC) { + xstate->signature_skipped = 2; + return unpack_Z_stream(xstate); + } + if (magic2 != GZIP_MAGIC) + goto bad_magic; + } +#endif + + total = 0; + + ALLOC_STATE; + to_read = -1; +// bytebuffer_max = 0x8000; + bytebuffer = xmalloc(bytebuffer_max); + gunzip_src_fd = xstate->src_fd; + + again: + if (!check_header_gzip(PASS_STATE xstate)) { + bb_error_msg("corrupted data"); + total = -1; + goto ret; + } + + n = inflate_unzip_internal(PASS_STATE xstate); + if (n < 0) { + total = -1; + goto ret; + } + total += n; + + if (!top_up(PASS_STATE 8)) { + bb_error_msg("corrupted data"); + total = -1; + goto ret; + } + + /* Validate decompression - crc */ + v32 = buffer_read_le_u32(PASS_STATE_ONLY); + if ((~gunzip_crc) != v32) { + bb_error_msg("crc error"); + total = -1; + goto ret; + } + + /* Validate decompression - size */ + v32 = buffer_read_le_u32(PASS_STATE_ONLY); + if ((uint32_t)gunzip_bytes_out != v32) { + bb_error_msg("incorrect length"); + total = -1; + } + + if (!top_up(PASS_STATE 2)) + goto ret; /* EOF */ + + if (bytebuffer[bytebuffer_offset] == 0x1f + && bytebuffer[bytebuffer_offset + 1] == 0x8b + ) { + bytebuffer_offset += 2; + goto again; + } + /* GNU gzip says: */ + /*bb_error_msg("decompression OK, trailing garbage ignored");*/ + + ret: + free(bytebuffer); + DEALLOC_STATE; + return total; +} diff --git a/busybox/archival/libarchive/decompress_uncompress.c b/busybox/archival/libarchive/decompress_uncompress.c new file mode 100644 index 00000000000000..1517559c64c353 --- /dev/null +++ b/busybox/archival/libarchive/decompress_uncompress.c @@ -0,0 +1,312 @@ +/* vi: set sw=4 ts=4: */ +/* + * uncompress for busybox -- (c) 2002 Robert Griebl + * + * based on the original compress42.c source + * (see disclaimer below) + */ +/* (N)compress42.c - File compression ala IEEE Computer, Mar 1992. + * + * Authors: + * Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) + * Jim McKie (decvax!mcvax!jim) + * Steve Davies (decvax!vax135!petsd!peora!srd) + * Ken Turkowski (decvax!decwrl!turtlevax!ken) + * James A. Woods (decvax!ihnp4!ames!jaw) + * Joe Orost (decvax!vax135!petsd!joe) + * Dave Mack (csu@alembic.acs.com) + * Peter Jannesen, Network Communication Systems + * (peter@ncs.nl) + * + * marc@suse.de : a small security fix for a buffer overflow + * + * [... History snipped ...] + */ +#include "libbb.h" +#include "bb_archive.h" + + +/* Default input buffer size */ +#define IBUFSIZ 2048 + +/* Default output buffer size */ +#define OBUFSIZ 2048 + +/* Defines for third byte of header */ +#define BIT_MASK 0x1f /* Mask for 'number of compresssion bits' */ + /* Masks 0x20 and 0x40 are free. */ + /* I think 0x20 should mean that there is */ + /* a fourth header byte (for expansion). */ +#define BLOCK_MODE 0x80 /* Block compression if table is full and */ + /* compression rate is dropping flush tables */ + /* the next two codes should not be changed lightly, as they must not */ + /* lie within the contiguous general code space. */ +#define FIRST 257 /* first free entry */ +#define CLEAR 256 /* table clear output code */ + +#define INIT_BITS 9 /* initial number of bits/code */ + + +/* machine variants which require cc -Dmachine: pdp11, z8000, DOS */ +#define HBITS 17 /* 50% occupancy */ +#define HSIZE (1<src_fd, inbuf, 1) != 1) { + bb_error_msg("short read"); + goto err; + } + + maxbits = inbuf[0] & BIT_MASK; + block_mode = inbuf[0] & BLOCK_MODE; + maxmaxcode = MAXCODE(maxbits); + + if (maxbits > BITS) { + bb_error_msg("compressed with %d bits, can only handle " + BITS_STR" bits", maxbits); + goto err; + } + + n_bits = INIT_BITS; + maxcode = MAXCODE(INIT_BITS) - 1; + bitmask = (1 << INIT_BITS) - 1; + oldcode = -1; + finchar = 0; + outpos = 0; + posbits = 0 << 3; + + free_ent = ((block_mode) ? FIRST : 256); + + /* As above, initialize the first 256 entries in the table. */ + /*clear_tab_prefixof(); - done by xzalloc */ + + { + int i; + for (i = 255; i >= 0; --i) + tab_suffixof(i) = (unsigned char) i; + } + + do { + resetbuf: + { + int i; + int e; + int o; + + o = posbits >> 3; + e = insize - o; + + for (i = 0; i < e; ++i) + inbuf[i] = inbuf[i + o]; + + insize = e; + posbits = 0; + } + + if (insize < (int) (IBUFSIZ + 64) - IBUFSIZ) { + rsize = safe_read(xstate->src_fd, inbuf + insize, IBUFSIZ); + if (rsize < 0) + bb_error_msg_and_die(bb_msg_read_error); + insize += rsize; + } + + inbits = ((rsize > 0) ? (insize - insize % n_bits) << 3 : + (insize << 3) - (n_bits - 1)); + + while (inbits > posbits) { + long code; + + if (free_ent > maxcode) { + posbits = + ((posbits - 1) + + ((n_bits << 3) - + (posbits - 1 + (n_bits << 3)) % (n_bits << 3))); + ++n_bits; + if (n_bits == maxbits) { + maxcode = maxmaxcode; + } else { + maxcode = MAXCODE(n_bits) - 1; + } + bitmask = (1 << n_bits) - 1; + goto resetbuf; + } + { + unsigned char *p = &inbuf[posbits >> 3]; + code = ((p[0] + | ((long) (p[1]) << 8) + | ((long) (p[2]) << 16)) >> (posbits & 0x7)) & bitmask; + } + posbits += n_bits; + + if (oldcode == -1) { + if (code >= 256) + bb_error_msg_and_die("corrupted data"); /* %ld", code); */ + oldcode = code; + finchar = (int) oldcode; + outbuf[outpos++] = (unsigned char) finchar; + continue; + } + + if (code == CLEAR && block_mode) { + clear_tab_prefixof(); + free_ent = FIRST - 1; + posbits = + ((posbits - 1) + + ((n_bits << 3) - + (posbits - 1 + (n_bits << 3)) % (n_bits << 3))); + n_bits = INIT_BITS; + maxcode = MAXCODE(INIT_BITS) - 1; + bitmask = (1 << INIT_BITS) - 1; + goto resetbuf; + } + + incode = code; + stackp = de_stack; + + /* Special case for KwKwK string. */ + if (code >= free_ent) { + if (code > free_ent) { +/* + unsigned char *p; + + posbits -= n_bits; + p = &inbuf[posbits >> 3]; + bb_error_msg + ("insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)", + insize, posbits, p[-1], p[0], p[1], p[2], p[3], + (posbits & 07)); +*/ + bb_error_msg("corrupted data"); + goto err; + } + + *--stackp = (unsigned char) finchar; + code = oldcode; + } + + /* Generate output characters in reverse order */ + while (code >= 256) { + if (stackp <= &htabof(0)) + bb_error_msg_and_die("corrupted data"); + *--stackp = tab_suffixof(code); + code = tab_prefixof(code); + } + + finchar = tab_suffixof(code); + *--stackp = (unsigned char) finchar; + + /* And put them out in forward order */ + { + int i; + + i = de_stack - stackp; + if (outpos + i >= OBUFSIZ) { + do { + if (i > OBUFSIZ - outpos) { + i = OBUFSIZ - outpos; + } + + if (i > 0) { + memcpy(outbuf + outpos, stackp, i); + outpos += i; + } + + if (outpos >= OBUFSIZ) { + xtransformer_write(xstate, outbuf, outpos); + IF_DESKTOP(total_written += outpos;) + outpos = 0; + } + stackp += i; + i = de_stack - stackp; + } while (i > 0); + } else { + memcpy(outbuf + outpos, stackp, i); + outpos += i; + } + } + + /* Generate the new entry. */ + if (free_ent < maxmaxcode) { + tab_prefixof(free_ent) = (unsigned short) oldcode; + tab_suffixof(free_ent) = (unsigned char) finchar; + free_ent++; + } + + /* Remember previous code. */ + oldcode = incode; + } + } while (rsize > 0); + + if (outpos > 0) { + xtransformer_write(xstate, outbuf, outpos); + IF_DESKTOP(total_written += outpos;) + } + + retval = IF_DESKTOP(total_written) + 0; + err: + free(inbuf); + free(outbuf); + free(htab); + free(codetab); + return retval; +} diff --git a/busybox/archival/libarchive/decompress_unlzma.c b/busybox/archival/libarchive/decompress_unlzma.c new file mode 100644 index 00000000000000..6886239d0351dd --- /dev/null +++ b/busybox/archival/libarchive/decompress_unlzma.c @@ -0,0 +1,518 @@ +/* vi: set sw=4 ts=4: */ +/* + * Small lzma deflate implementation. + * Copyright (C) 2006 Aurelien Jacobs + * + * Based on LzmaDecode.c from the LZMA SDK 4.22 (http://www.7-zip.org/) + * Copyright (C) 1999-2005 Igor Pavlov + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "bb_archive.h" + +#if 0 +# define dbg(...) bb_error_msg(__VA_ARGS__) +#else +# define dbg(...) ((void)0) +#endif + + +#if ENABLE_FEATURE_LZMA_FAST +# define speed_inline ALWAYS_INLINE +# define size_inline +#else +# define speed_inline +# define size_inline ALWAYS_INLINE +#endif + + +typedef struct { + int fd; + uint8_t *ptr; + +/* Was keeping rc on stack in unlzma and separately allocating buffer, + * but with "buffer 'attached to' allocated rc" code is smaller: */ + /* uint8_t *buffer; */ +#define RC_BUFFER ((uint8_t*)(rc+1)) + + uint8_t *buffer_end; + +/* Had provisions for variable buffer, but we don't need it here */ + /* int buffer_size; */ +#define RC_BUFFER_SIZE 0x10000 + + uint32_t code; + uint32_t range; + uint32_t bound; +} rc_t; + +#define RC_TOP_BITS 24 +#define RC_MOVE_BITS 5 +#define RC_MODEL_TOTAL_BITS 11 + + +/* Called once in rc_do_normalize() */ +static void rc_read(rc_t *rc) +{ + int buffer_size = safe_read(rc->fd, RC_BUFFER, RC_BUFFER_SIZE); +//TODO: return -1 instead +//This will make unlzma delete broken unpacked file on unpack errors + if (buffer_size <= 0) + bb_error_msg_and_die("unexpected EOF"); + rc->buffer_end = RC_BUFFER + buffer_size; + rc->ptr = RC_BUFFER; +} + +/* Called twice, but one callsite is in speed_inline'd rc_is_bit_1() */ +static void rc_do_normalize(rc_t *rc) +{ + if (rc->ptr >= rc->buffer_end) + rc_read(rc); + rc->range <<= 8; + rc->code = (rc->code << 8) | *rc->ptr++; +} +static ALWAYS_INLINE void rc_normalize(rc_t *rc) +{ + if (rc->range < (1 << RC_TOP_BITS)) { + rc_do_normalize(rc); + } +} + +/* Called once */ +static ALWAYS_INLINE rc_t* rc_init(int fd) /*, int buffer_size) */ +{ + int i; + rc_t *rc; + + rc = xzalloc(sizeof(*rc) + RC_BUFFER_SIZE); + + rc->fd = fd; + /* rc->ptr = rc->buffer_end; */ + + for (i = 0; i < 5; i++) { + rc_do_normalize(rc); + } + rc->range = 0xffffffff; + return rc; +} + +/* Called once */ +static ALWAYS_INLINE void rc_free(rc_t *rc) +{ + free(rc); +} + +/* rc_is_bit_1 is called 9 times */ +static speed_inline int rc_is_bit_1(rc_t *rc, uint16_t *p) +{ + rc_normalize(rc); + rc->bound = *p * (rc->range >> RC_MODEL_TOTAL_BITS); + if (rc->code < rc->bound) { + rc->range = rc->bound; + *p += ((1 << RC_MODEL_TOTAL_BITS) - *p) >> RC_MOVE_BITS; + return 0; + } + rc->range -= rc->bound; + rc->code -= rc->bound; + *p -= *p >> RC_MOVE_BITS; + return 1; +} + +/* Called 4 times in unlzma loop */ +static ALWAYS_INLINE int rc_get_bit(rc_t *rc, uint16_t *p, int *symbol) +{ + int ret = rc_is_bit_1(rc, p); + *symbol = *symbol * 2 + ret; + return ret; +} + +/* Called once */ +static ALWAYS_INLINE int rc_direct_bit(rc_t *rc) +{ + rc_normalize(rc); + rc->range >>= 1; + if (rc->code >= rc->range) { + rc->code -= rc->range; + return 1; + } + return 0; +} + +/* Called twice */ +static speed_inline void +rc_bit_tree_decode(rc_t *rc, uint16_t *p, int num_levels, int *symbol) +{ + int i = num_levels; + + *symbol = 1; + while (i--) + rc_get_bit(rc, p + *symbol, symbol); + *symbol -= 1 << num_levels; +} + + +typedef struct { + uint8_t pos; + uint32_t dict_size; + uint64_t dst_size; +} PACKED lzma_header_t; + + +/* #defines will force compiler to compute/optimize each one with each usage. + * Have heart and use enum instead. */ +enum { + LZMA_BASE_SIZE = 1846, + LZMA_LIT_SIZE = 768, + + LZMA_NUM_POS_BITS_MAX = 4, + + LZMA_LEN_NUM_LOW_BITS = 3, + LZMA_LEN_NUM_MID_BITS = 3, + LZMA_LEN_NUM_HIGH_BITS = 8, + + LZMA_LEN_CHOICE = 0, + LZMA_LEN_CHOICE_2 = (LZMA_LEN_CHOICE + 1), + LZMA_LEN_LOW = (LZMA_LEN_CHOICE_2 + 1), + LZMA_LEN_MID = (LZMA_LEN_LOW \ + + (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_LOW_BITS))), + LZMA_LEN_HIGH = (LZMA_LEN_MID \ + + (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_MID_BITS))), + LZMA_NUM_LEN_PROBS = (LZMA_LEN_HIGH + (1 << LZMA_LEN_NUM_HIGH_BITS)), + + LZMA_NUM_STATES = 12, + LZMA_NUM_LIT_STATES = 7, + + LZMA_START_POS_MODEL_INDEX = 4, + LZMA_END_POS_MODEL_INDEX = 14, + LZMA_NUM_FULL_DISTANCES = (1 << (LZMA_END_POS_MODEL_INDEX >> 1)), + + LZMA_NUM_POS_SLOT_BITS = 6, + LZMA_NUM_LEN_TO_POS_STATES = 4, + + LZMA_NUM_ALIGN_BITS = 4, + + LZMA_MATCH_MIN_LEN = 2, + + LZMA_IS_MATCH = 0, + LZMA_IS_REP = (LZMA_IS_MATCH + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX)), + LZMA_IS_REP_G0 = (LZMA_IS_REP + LZMA_NUM_STATES), + LZMA_IS_REP_G1 = (LZMA_IS_REP_G0 + LZMA_NUM_STATES), + LZMA_IS_REP_G2 = (LZMA_IS_REP_G1 + LZMA_NUM_STATES), + LZMA_IS_REP_0_LONG = (LZMA_IS_REP_G2 + LZMA_NUM_STATES), + LZMA_POS_SLOT = (LZMA_IS_REP_0_LONG \ + + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX)), + LZMA_SPEC_POS = (LZMA_POS_SLOT \ + + (LZMA_NUM_LEN_TO_POS_STATES << LZMA_NUM_POS_SLOT_BITS)), + LZMA_ALIGN = (LZMA_SPEC_POS \ + + LZMA_NUM_FULL_DISTANCES - LZMA_END_POS_MODEL_INDEX), + LZMA_LEN_CODER = (LZMA_ALIGN + (1 << LZMA_NUM_ALIGN_BITS)), + LZMA_REP_LEN_CODER = (LZMA_LEN_CODER + LZMA_NUM_LEN_PROBS), + LZMA_LITERAL = (LZMA_REP_LEN_CODER + LZMA_NUM_LEN_PROBS), +}; + + +IF_DESKTOP(long long) int FAST_FUNC +unpack_lzma_stream(transformer_state_t *xstate) +{ + IF_DESKTOP(long long total_written = 0;) + lzma_header_t header; + int lc, pb, lp; + uint32_t pos_state_mask; + uint32_t literal_pos_mask; + uint16_t *p; + rc_t *rc; + int i; + uint8_t *buffer; + uint32_t buffer_size; + uint8_t previous_byte = 0; + size_t buffer_pos = 0, global_pos = 0; + int len = 0; + int state = 0; + uint32_t rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; + + if (full_read(xstate->src_fd, &header, sizeof(header)) != sizeof(header) + || header.pos >= (9 * 5 * 5) + ) { + bb_error_msg("bad lzma header"); + return -1; + } + + i = header.pos / 9; + lc = header.pos % 9; + pb = i / 5; + lp = i % 5; + 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); + + if (header.dict_size == 0) + header.dict_size++; + + buffer_size = MIN(header.dst_size, header.dict_size); + buffer = xmalloc(buffer_size); + + { + 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(xstate->src_fd); /*, RC_BUFFER_SIZE); */ + + while (global_pos + buffer_pos < header.dst_size) { + int pos_state = (buffer_pos + global_pos) & pos_state_mask; + uint16_t *prob = p + LZMA_IS_MATCH + (state << LZMA_NUM_POS_BITS_MAX) + pos_state; + + if (!rc_is_bit_1(rc, prob)) { + static const char next_state[LZMA_NUM_STATES] = + { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5 }; + int mi = 1; + + prob = (p + LZMA_LITERAL + + (LZMA_LIT_SIZE * ((((buffer_pos + global_pos) & literal_pos_mask) << lc) + + (previous_byte >> (8 - lc)) + ) + ) + ); + + if (state >= LZMA_NUM_LIT_STATES) { + int match_byte; + uint32_t pos; + + pos = buffer_pos - rep0; + if ((int32_t)pos < 0) + pos += header.dict_size; + match_byte = buffer[pos]; + do { + int bit; + + match_byte <<= 1; + bit = match_byte & 0x100; + bit ^= (rc_get_bit(rc, prob + 0x100 + bit + mi, &mi) << 8); /* 0x100 or 0 */ + if (bit) + break; + } while (mi < 0x100); + } + while (mi < 0x100) { + rc_get_bit(rc, prob + mi, &mi); + } + + state = next_state[state]; + + previous_byte = (uint8_t) mi; +#if ENABLE_FEATURE_LZMA_FAST + one_byte1: + buffer[buffer_pos++] = previous_byte; + if (buffer_pos == header.dict_size) { + buffer_pos = 0; + global_pos += 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;) + } +#else + len = 1; + goto one_byte2; +#endif + } else { + int num_bits; + int offset; + uint16_t *prob2; +#define prob_len prob2 + + prob2 = p + LZMA_IS_REP + state; + if (!rc_is_bit_1(rc, prob2)) { + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + state = state < LZMA_NUM_LIT_STATES ? 0 : 3; + prob2 = p + LZMA_LEN_CODER; + } else { + prob2 += LZMA_IS_REP_G0 - LZMA_IS_REP; + if (!rc_is_bit_1(rc, prob2)) { + prob2 = (p + LZMA_IS_REP_0_LONG + + (state << LZMA_NUM_POS_BITS_MAX) + + pos_state + ); + if (!rc_is_bit_1(rc, prob2)) { +#if ENABLE_FEATURE_LZMA_FAST + uint32_t pos; + state = state < LZMA_NUM_LIT_STATES ? 9 : 11; + + pos = buffer_pos - rep0; + if ((int32_t)pos < 0) { + pos += header.dict_size; + /* see unzip_bad_lzma_2.zip: */ + if (pos >= buffer_size) + goto bad; + } + previous_byte = buffer[pos]; + goto one_byte1; +#else + state = state < LZMA_NUM_LIT_STATES ? 9 : 11; + len = 1; + goto string; +#endif + } + } else { + uint32_t distance; + + prob2 += LZMA_IS_REP_G1 - LZMA_IS_REP_G0; + distance = rep1; + if (rc_is_bit_1(rc, prob2)) { + prob2 += LZMA_IS_REP_G2 - LZMA_IS_REP_G1; + distance = rep2; + if (rc_is_bit_1(rc, prob2)) { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < LZMA_NUM_LIT_STATES ? 8 : 11; + prob2 = p + LZMA_REP_LEN_CODER; + } + + prob_len = prob2 + LZMA_LEN_CHOICE; + num_bits = LZMA_LEN_NUM_LOW_BITS; + if (!rc_is_bit_1(rc, prob_len)) { + prob_len += LZMA_LEN_LOW - LZMA_LEN_CHOICE + + (pos_state << LZMA_LEN_NUM_LOW_BITS); + offset = 0; + } else { + prob_len += LZMA_LEN_CHOICE_2 - LZMA_LEN_CHOICE; + if (!rc_is_bit_1(rc, prob_len)) { + prob_len += LZMA_LEN_MID - LZMA_LEN_CHOICE_2 + + (pos_state << LZMA_LEN_NUM_MID_BITS); + offset = 1 << LZMA_LEN_NUM_LOW_BITS; + num_bits += LZMA_LEN_NUM_MID_BITS - LZMA_LEN_NUM_LOW_BITS; + } else { + prob_len += LZMA_LEN_HIGH - LZMA_LEN_CHOICE_2; + offset = ((1 << LZMA_LEN_NUM_LOW_BITS) + + (1 << LZMA_LEN_NUM_MID_BITS)); + num_bits += LZMA_LEN_NUM_HIGH_BITS - LZMA_LEN_NUM_LOW_BITS; + } + } + rc_bit_tree_decode(rc, prob_len, num_bits, &len); + len += offset; + + if (state < 4) { + int pos_slot; + uint16_t *prob3; + + state += LZMA_NUM_LIT_STATES; + prob3 = p + LZMA_POS_SLOT + + ((len < LZMA_NUM_LEN_TO_POS_STATES ? len : + LZMA_NUM_LEN_TO_POS_STATES - 1) + << LZMA_NUM_POS_SLOT_BITS); + rc_bit_tree_decode(rc, prob3, + LZMA_NUM_POS_SLOT_BITS, &pos_slot); + rep0 = pos_slot; + if (pos_slot >= LZMA_START_POS_MODEL_INDEX) { + int i2, mi2, num_bits2 = (pos_slot >> 1) - 1; + rep0 = 2 | (pos_slot & 1); + if (pos_slot < LZMA_END_POS_MODEL_INDEX) { + rep0 <<= num_bits2; + prob3 = p + LZMA_SPEC_POS + rep0 - pos_slot - 1; + } else { + for (; num_bits2 != LZMA_NUM_ALIGN_BITS; num_bits2--) + rep0 = (rep0 << 1) | rc_direct_bit(rc); + rep0 <<= LZMA_NUM_ALIGN_BITS; + if ((int32_t)rep0 < 0) { + dbg("%d rep0:%d", __LINE__, rep0); + goto bad; + } + prob3 = p + LZMA_ALIGN; + } + i2 = 1; + mi2 = 1; + while (num_bits2--) { + if (rc_get_bit(rc, prob3 + mi2, &mi2)) + rep0 |= i2; + i2 <<= 1; + } + } + if (++rep0 == 0) + break; + } + + len += LZMA_MATCH_MIN_LEN; + /* + * LZMA SDK has this optimized: + * it precalculates size and copies many bytes + * in a loop with simpler checks, a-la: + * do + * *(dest) = *(dest + ofs); + * while (++dest != lim); + * and + * do { + * buffer[buffer_pos++] = buffer[pos]; + * if (++pos == header.dict_size) + * pos = 0; + * } while (--cur_len != 0); + * Our code is slower (more checks per byte copy): + */ + IF_NOT_FEATURE_LZMA_FAST(string:) + do { + uint32_t pos = buffer_pos - rep0; + if ((int32_t)pos < 0) { + pos += header.dict_size; + /* bug 10436 has an example file where this triggers: */ + //if ((int32_t)pos < 0) + // goto bad; + /* more stringent test (see unzip_bad_lzma_1.zip): */ + if (pos >= buffer_size) + goto bad; + } + previous_byte = buffer[pos]; + IF_NOT_FEATURE_LZMA_FAST(one_byte2:) + buffer[buffer_pos++] = previous_byte; + if (buffer_pos == header.dict_size) { + buffer_pos = 0; + global_pos += 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"? + * It probably should, but it is a "do we accidentally + * unpack more bytes than expected?" check - which + * never happens for well-formed compression data... + */ + } + } + + { + IF_NOT_DESKTOP(int total_written = 0; /* success */) + IF_DESKTOP(total_written += buffer_pos;) + if (transformer_write(xstate, buffer, buffer_pos) != (ssize_t)buffer_pos) { + bad: + /* One of our users, bbunpack(), expects _us_ to emit + * the error message (since it's the best place to give + * potentially more detailed information). + * Do not fail silently. + */ + bb_error_msg("corrupted data"); + total_written = -1; /* failure */ + } + rc_free(rc); + free(p); + free(buffer); + return total_written; + } +} diff --git a/busybox/archival/libarchive/decompress_unxz.c b/busybox/archival/libarchive/decompress_unxz.c new file mode 100644 index 00000000000000..8ae7a275bfd71c --- /dev/null +++ b/busybox/archival/libarchive/decompress_unxz.c @@ -0,0 +1,136 @@ +/* + * This file uses XZ Embedded library code which is written + * by Lasse Collin + * and Igor Pavlov + * + * See README file in unxz/ directory for more information. + * + * This file is: + * Copyright (C) 2010 Denys Vlasenko + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "bb_archive.h" + +#define XZ_FUNC FAST_FUNC +#define XZ_EXTERN static + +#define XZ_DEC_DYNALLOC + +/* Skip check (rather than fail) of unsupported hash functions */ +#define XZ_DEC_ANY_CHECK 1 + +/* We use our own crc32 function */ +#define XZ_INTERNAL_CRC32 0 +static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc) +{ + return ~crc32_block_endian0(~crc, buf, size, global_crc32_table); +} + +/* We use arch-optimized unaligned fixed-endian accessors. + * They have been moved to libbb (proved to be useful elsewhere as well), + * just check that we have them defined: + */ +#if !defined(get_unaligned_le32) \ + || !defined(get_unaligned_be32) \ + || !defined(put_unaligned_le32) \ + || !defined(put_unaligned_be32) +# error get_unaligned_le32 accessors are not defined +#endif + +#include "unxz/xz_dec_bcj.c" +#include "unxz/xz_dec_lzma2.c" +#include "unxz/xz_dec_stream.c" + +IF_DESKTOP(long long) int FAST_FUNC +unpack_xz_stream(transformer_state_t *xstate) +{ + enum xz_ret xz_result; + struct xz_buf iobuf; + struct xz_dec *state; + unsigned char *membuf; + IF_DESKTOP(long long) int total = 0; + + if (!global_crc32_table) + global_crc32_new_table_le(); + + memset(&iobuf, 0, sizeof(iobuf)); + membuf = xmalloc(2 * BUFSIZ); + iobuf.in = membuf; + iobuf.out = membuf + BUFSIZ; + iobuf.out_size = BUFSIZ; + + if (!xstate || xstate->signature_skipped) { + /* Preload XZ file signature */ + strcpy((char*)membuf, HEADER_MAGIC); + iobuf.in_size = HEADER_MAGIC_SIZE; + } /* else: let xz code read & check it */ + + /* Limit memory usage to about 64 MiB. */ + state = xz_dec_init(XZ_DYNALLOC, 64*1024*1024); + + xz_result = X_OK; + while (1) { + if (iobuf.in_pos == iobuf.in_size) { + int rd = safe_read(xstate->src_fd, membuf, BUFSIZ); + if (rd < 0) { + bb_error_msg(bb_msg_read_error); + total = -1; + break; + } + if (rd == 0 && xz_result == XZ_STREAM_END) + break; + iobuf.in_size = rd; + iobuf.in_pos = 0; + } + if (xz_result == XZ_STREAM_END) { + /* + * Try to start decoding next concatenated stream. + * Stream padding must always be a multiple of four + * bytes to preserve four-byte alignment. To keep the + * code slightly smaller, we aren't as strict here as + * the .xz spec requires. We just skip all zero-bytes + * without checking the alignment and thus can accept + * files that aren't valid, e.g. the XZ utils test + * files bad-0pad-empty.xz and bad-0catpad-empty.xz. + */ + do { + if (membuf[iobuf.in_pos] != 0) { + xz_dec_reset(state); + goto do_run; + } + iobuf.in_pos++; + } while (iobuf.in_pos < iobuf.in_size); + } + do_run: +// bb_error_msg(">in pos:%d size:%d out pos:%d size:%d", +// iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size); + xz_result = xz_dec_run(state, &iobuf); +// bb_error_msg("file_header->name) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} diff --git a/busybox/archival/libarchive/filter_accept_list.c b/busybox/archival/libarchive/filter_accept_list.c new file mode 100644 index 00000000000000..32f806574508f7 --- /dev/null +++ b/busybox/archival/libarchive/filter_accept_list.c @@ -0,0 +1,18 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "bb_archive.h" + +/* + * Accept names that are in the accept list, ignoring reject list. + */ +char FAST_FUNC filter_accept_list(archive_handle_t *archive_handle) +{ + if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} diff --git a/busybox/archival/libarchive/filter_accept_list_reassign.c b/busybox/archival/libarchive/filter_accept_list_reassign.c new file mode 100644 index 00000000000000..826c5c29dd55dc --- /dev/null +++ b/busybox/archival/libarchive/filter_accept_list_reassign.c @@ -0,0 +1,60 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "bb_archive.h" + +/* Built and used only if ENABLE_DPKG || ENABLE_DPKG_DEB */ + +/* + * Reassign the subarchive metadata parser based on the filename extension + * e.g. if its a .tar.gz modify archive_handle->sub_archive to process a .tar.gz + * or if its a .tar.bz2 make archive_handle->sub_archive handle that + */ +char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle) +{ + /* Check the file entry is in the accept list */ + if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) { + const char *name_ptr; + + /* Find extension */ + name_ptr = strrchr(archive_handle->file_header->name, '.'); + if (!name_ptr) + return EXIT_FAILURE; + 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 + ) { + archive_handle->dpkg__action_data_subarchive = get_header_tar_gz; + return EXIT_SUCCESS; + } + if (ENABLE_FEATURE_SEAMLESS_BZ2 + && strcmp(name_ptr, "bz2") == 0 + ) { + archive_handle->dpkg__action_data_subarchive = get_header_tar_bz2; + return EXIT_SUCCESS; + } + if (ENABLE_FEATURE_SEAMLESS_LZMA + && strcmp(name_ptr, "lzma") == 0 + ) { + 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/busybox/archival/libarchive/filter_accept_reject_list.c b/busybox/archival/libarchive/filter_accept_reject_list.c new file mode 100644 index 00000000000000..939e626faf01b4 --- /dev/null +++ b/busybox/archival/libarchive/filter_accept_reject_list.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "bb_archive.h" + +/* + * Accept names that are in the accept list and not in the reject list + */ +char FAST_FUNC filter_accept_reject_list(archive_handle_t *archive_handle) +{ + const char *key; + const llist_t *reject_entry; + const llist_t *accept_entry; + + key = archive_handle->file_header->name; + + /* If the key is in a reject list fail */ + reject_entry = find_list_entry2(archive_handle->reject, key); + if (reject_entry) { + return EXIT_FAILURE; + } + + /* Fail if an accept list was specified and the key wasnt in there */ + if (archive_handle->accept) { + accept_entry = find_list_entry2(archive_handle->accept, key); + if (!accept_entry) { + return EXIT_FAILURE; + } + } + + /* Accepted */ + return EXIT_SUCCESS; +} diff --git a/busybox/archival/libarchive/find_list_entry.c b/busybox/archival/libarchive/find_list_entry.c new file mode 100644 index 00000000000000..37726bd3d223a3 --- /dev/null +++ b/busybox/archival/libarchive/find_list_entry.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2002 by Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include +#include "libbb.h" +#include "bb_archive.h" + +/* Find a string in a shell pattern list */ +const llist_t* FAST_FUNC find_list_entry(const llist_t *list, const char *filename) +{ + while (list) { + if (fnmatch(list->data, filename, 0) == 0) { + return list; + } + list = list->link; + } + return NULL; +} + +/* Same, but compares only path components present in pattern + * (extra trailing path components in filename are assumed to match) + */ +const llist_t* FAST_FUNC find_list_entry2(const llist_t *list, const char *filename) +{ + char buf[PATH_MAX]; + int pattern_slash_cnt; + const char *c; + char *d; + + while (list) { + c = list->data; + pattern_slash_cnt = 0; + while (*c) + if (*c++ == '/') pattern_slash_cnt++; + c = filename; + d = buf; + /* paranoia is better than buffer overflows */ + while (*c && d != buf + sizeof(buf)-1) { + if (*c == '/' && --pattern_slash_cnt < 0) + break; + *d++ = *c++; + } + *d = '\0'; + if (fnmatch(list->data, buf, 0) == 0) { + return list; + } + list = list->link; + } + return NULL; +} diff --git a/busybox/archival/libarchive/get_header_ar.c b/busybox/archival/libarchive/get_header_ar.c new file mode 100644 index 00000000000000..7ce9c615c69661 --- /dev/null +++ b/busybox/archival/libarchive/get_header_ar.c @@ -0,0 +1,142 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 2001 Glenn McGrath. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "bb_archive.h" +#include "ar.h" + +/* WARNING: Clobbers str[len], so fields must be read in reverse order! */ +static unsigned read_num(char *str, int base, int len) +{ + int err; + + /* ar fields are fixed length text strings (padded with spaces). + * Ensure bb_strtou doesn't read past the field in case the full + * width is used. */ + str[len] = 0; + + /* This code works because + * on misformatted numbers bb_strtou returns all-ones */ + err = bb_strtou(str, NULL, base); + if (err == -1) + bb_error_msg_and_die("invalid ar header"); + return err; +} + +char FAST_FUNC get_header_ar(archive_handle_t *archive_handle) +{ + file_header_t *typed = archive_handle->file_header; + unsigned size; + union { + char raw[60]; + struct ar_header formatted; + } ar; + + /* dont use xread as we want to handle the error ourself */ + if (read(archive_handle->src_fd, ar.raw, 60) != 60) { + /* End Of File */ + return EXIT_FAILURE; + } + + /* ar header starts on an even byte (2 byte aligned) + * '\n' is used for padding + */ + if (ar.raw[0] == '\n') { + /* fix up the header, we started reading 1 byte too early */ + memmove(ar.raw, &ar.raw[1], 59); + ar.raw[59] = xread_char(archive_handle->src_fd); + archive_handle->offset++; + } + archive_handle->offset += 60; + + if (ar.formatted.magic[0] != '`' || ar.formatted.magic[1] != '\n') + bb_error_msg_and_die("invalid ar header"); + + /* + * Note that the fields MUST be read in reverse order as + * read_num() clobbers the next byte after the field! + * Order is: name, date, uid, gid, mode, size, magic. + */ + typed->size = size = read_num(ar.formatted.size, 10, + sizeof(ar.formatted.size)); + + /* special filenames have '/' as the first character */ + if (ar.formatted.name[0] == '/') { + if (ar.formatted.name[1] == ' ') { + /* This is the index of symbols in the file for compilers */ + data_skip(archive_handle); + archive_handle->offset += size; + return get_header_ar(archive_handle); /* Return next header */ + } +#if ENABLE_FEATURE_AR_LONG_FILENAMES + if (ar.formatted.name[1] == '/') { + /* If the second char is a '/' then this entries data section + * stores long filename for multiple entries, they are stored + * in static variable long_names for use in future entries + */ + archive_handle->ar__long_name_size = size; + free(archive_handle->ar__long_names); + archive_handle->ar__long_names = xzalloc(size + 1); + xread(archive_handle->src_fd, archive_handle->ar__long_names, size); + archive_handle->offset += size; + /* Return next header */ + return get_header_ar(archive_handle); + } +#else + bb_error_msg_and_die("long filenames not supported"); +#endif + } + /* Only size is always present, the rest may be missing in + * long filename pseudo file. Thus we decode the rest + * after dealing with long filename pseudo file. + */ + typed->mode = read_num(ar.formatted.mode, 8, sizeof(ar.formatted.mode)); + typed->gid = read_num(ar.formatted.gid, 10, sizeof(ar.formatted.gid)); + typed->uid = read_num(ar.formatted.uid, 10, sizeof(ar.formatted.uid)); + typed->mtime = read_num(ar.formatted.date, 10, sizeof(ar.formatted.date)); + +#if ENABLE_FEATURE_AR_LONG_FILENAMES + if (ar.formatted.name[0] == '/') { + unsigned long_offset; + + /* The number after the '/' indicates the offset in the ar data section + * (saved in ar__long_names) that contains the real filename */ + long_offset = read_num(&ar.formatted.name[1], 10, + sizeof(ar.formatted.name) - 1); + if (long_offset >= archive_handle->ar__long_name_size) { + bb_error_msg_and_die("can't resolve long filename"); + } + typed->name = xstrdup(archive_handle->ar__long_names + long_offset); + } else +#endif + { + /* short filenames */ + typed->name = xstrndup(ar.formatted.name, 16); + } + + typed->name[strcspn(typed->name, " /")] = '\0'; + + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { + archive_handle->action_header(typed); +#if ENABLE_DPKG || ENABLE_DPKG_DEB + if (archive_handle->dpkg__sub_archive) { + struct archive_handle_t *sa = archive_handle->dpkg__sub_archive; + while (archive_handle->dpkg__action_data_subarchive(sa) == EXIT_SUCCESS) + continue; + create_links_from_list(sa->link_placeholders); + } else +#endif + archive_handle->action_data(archive_handle); + } else { + data_skip(archive_handle); + } + + archive_handle->offset += typed->size; + /* Set the file pointer to the correct spot, we may have been reading a compressed file */ + lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET); + + return EXIT_SUCCESS; +} diff --git a/busybox/archival/libarchive/get_header_cpio.c b/busybox/archival/libarchive/get_header_cpio.c new file mode 100644 index 00000000000000..75fc6a40679823 --- /dev/null +++ b/busybox/archival/libarchive/get_header_cpio.c @@ -0,0 +1,191 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 2002 Laurence Anderson + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "bb_archive.h" + +typedef struct hardlinks_t { + struct hardlinks_t *next; + int inode; /* TODO: must match maj/min too! */ + int mode ; + int mtime; /* These three are useful only in corner case */ + int uid ; /* of hardlinks with zero size body */ + int gid ; + char name[1]; +} hardlinks_t; + +char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle) +{ + file_header_t *file_header = archive_handle->file_header; + char cpio_header[110]; + int namesize; + int major, minor, nlink, mode, inode; + unsigned size, uid, gid, mtime; + + /* There can be padding before archive header */ + data_align(archive_handle, 4); + + size = full_read(archive_handle->src_fd, cpio_header, 110); + if (size == 0) { + goto create_hardlinks; + } + if (size != 110) { + bb_error_msg_and_die("short read"); + } + archive_handle->offset += 110; + + 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"); + } + + if (sscanf(cpio_header + 6, + "%8x" "%8x" "%8x" "%8x" + "%8x" "%8x" "%8x" /*maj,min:*/ "%*16c" + /*rmaj,rmin:*/"%8x" "%8x" "%8x" /*chksum: "%*8c"*/, + &inode, &mode, &uid, &gid, + &nlink, &mtime, &size, + &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; + file_header->size = size; + + namesize &= 0x1fff; /* paranoia: limit names to 8k chars */ + file_header->name = xzalloc(namesize + 1); + /* Read in filename */ + xread(archive_handle->src_fd, file_header->name, namesize); + if (file_header->name[0] == '/') { + /* Testcase: echo /etc/hosts | cpio -pvd /tmp + * Without this code, it tries to unpack /etc/hosts + * into "/etc/hosts", not "etc/hosts". + */ + char *p = file_header->name; + do p++; while (*p == '/'); + overlapping_strcpy(file_header->name, p); + } + archive_handle->offset += namesize; + + /* Update offset amount and skip padding before file contents */ + data_align(archive_handle, 4); + + 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; + } + + file_header->link_target = NULL; + if (S_ISLNK(file_header->mode)) { + file_header->size &= 0x1fff; /* paranoia: limit names to 8k chars */ + file_header->link_target = xzalloc(file_header->size + 1); + xread(archive_handle->src_fd, file_header->link_target, file_header->size); + archive_handle->offset += file_header->size; + file_header->size = 0; /* Stop possible seeks in future */ + } + +// TODO: data_extract_all can't deal with hardlinks to non-files... +// when fixed, change S_ISREG to !S_ISDIR here + + if (nlink > 1 && S_ISREG(file_header->mode)) { + hardlinks_t *new = xmalloc(sizeof(*new) + namesize); + new->inode = inode; + new->mode = mode ; + new->mtime = mtime; + new->uid = uid ; + new->gid = gid ; + strcpy(new->name, file_header->name); + /* Put file on a linked list for later */ + if (size == 0) { + new->next = archive_handle->cpio__hardlinks_to_create; + archive_handle->cpio__hardlinks_to_create = new; + return EXIT_SUCCESS; /* Skip this one */ + /* TODO: this breaks cpio -t (it does not show hardlinks) */ + } + new->next = archive_handle->cpio__created_hardlinks; + archive_handle->cpio__created_hardlinks = new; + } + file_header->device = makedev(major, minor); + + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { + archive_handle->action_data(archive_handle); +//TODO: run "echo /etc/hosts | cpio -pv /tmp" twice. On 2nd run: +//cpio: etc/hosts not created: newer or same age file exists +//etc/hosts <-- should NOT show it +//2 blocks <-- should say "0 blocks" + archive_handle->action_header(file_header); + } else { + data_skip(archive_handle); + } + + archive_handle->offset += file_header->size; + + free(file_header->link_target); + free(file_header->name); + file_header->link_target = NULL; + file_header->name = NULL; + + return EXIT_SUCCESS; + + create_hardlinks: + free(file_header->link_target); + free(file_header->name); + + while (archive_handle->cpio__hardlinks_to_create) { + hardlinks_t *cur; + hardlinks_t *make_me = archive_handle->cpio__hardlinks_to_create; + + archive_handle->cpio__hardlinks_to_create = make_me->next; + + memset(file_header, 0, sizeof(*file_header)); + file_header->mtime = make_me->mtime; + file_header->name = make_me->name; + file_header->mode = make_me->mode; + file_header->uid = make_me->uid; + file_header->gid = make_me->gid; + /*file_header->size = 0;*/ + /*file_header->link_target = NULL;*/ + + /* Try to find a file we are hardlinked to */ + cur = archive_handle->cpio__created_hardlinks; + while (cur) { + /* TODO: must match maj/min too! */ + if (cur->inode == make_me->inode) { + file_header->link_target = cur->name; + /* link_target != NULL, size = 0: "I am a hardlink" */ + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) + archive_handle->action_data(archive_handle); + free(make_me); + goto next_link; + } + cur = cur->next; + } + /* Oops... no file with such inode was created... do it now + * (happens when hardlinked files are empty (zero length)) */ + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) + archive_handle->action_data(archive_handle); + /* Move to the list of created hardlinked files */ + make_me->next = archive_handle->cpio__created_hardlinks; + archive_handle->cpio__created_hardlinks = make_me; + next_link: ; + } + + while (archive_handle->cpio__created_hardlinks) { + hardlinks_t *p = archive_handle->cpio__created_hardlinks; + archive_handle->cpio__created_hardlinks = p->next; + free(p); + } + + return EXIT_FAILURE; /* "No more files to process" */ +} diff --git a/busybox/archival/libarchive/get_header_tar.c b/busybox/archival/libarchive/get_header_tar.c new file mode 100644 index 00000000000000..5c495e14e66b89 --- /dev/null +++ b/busybox/archival/libarchive/get_header_tar.c @@ -0,0 +1,482 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * FIXME: + * In privileged mode if uname and gname map to a uid and gid then use the + * mapped value instead of the uid/gid values in tar header + * + * References: + * GNU tar and star man pages, + * Opengroup's ustar interchange format, + * http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html + */ +#include "libbb.h" +#include "bb_archive.h" + +typedef uint32_t aliased_uint32_t FIX_ALIASING; +typedef off_t aliased_off_t FIX_ALIASING; + +/* NB: _DESTROYS_ str[len] character! */ +static unsigned long long getOctal(char *str, int len) +{ + unsigned long long v; + char *end; + /* NB: leading spaces are allowed. Using strtoull to handle that. + * The downside is that we accept e.g. "-123" too :( + */ + str[len] = '\0'; + v = strtoull(str, &end, 8); + /* std: "Each numeric field is terminated by one or more + * or NUL characters". We must support ' '! */ + if (*end != '\0' && *end != ' ') { + int8_t first = str[0]; + if (!(first & 0x80)) + bb_error_msg_and_die("corrupted octal value in tar header"); + /* + * GNU tar uses "base-256 encoding" for very large numbers. + * Encoding is binary, with highest bit always set as a marker + * and sign in next-highest bit: + * 80 00 .. 00 - zero + * bf ff .. ff - largest positive number + * ff ff .. ff - minus 1 + * c0 00 .. 00 - smallest negative number + * + * Example of tar file with 8914993153 (0x213600001) byte file. + * Field starts at offset 7c: + * 00070 30 30 30 00 30 30 30 30 30 30 30 00 80 00 00 00 |000.0000000.....| + * 00080 00 00 00 02 13 60 00 01 31 31 31 32 30 33 33 36 |.....`..11120336| + * + * NB: tarballs with NEGATIVE unix times encoded that way were seen! + */ + /* Sign-extend 7bit 'first' to 64bit 'v' (that is, using 6th bit as sign): */ + first <<= 1; + first >>= 1; /* now 7th bit = 6th bit */ + v = first; /* sign-extend 8 bits to 64 */ + while (--len != 0) + v = (v << 8) + (uint8_t) *++str; + } + return v; +} +#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; + + p = buf = xmalloc(blk_sz + 1); + xread(archive_handle->src_fd, buf, blk_sz); + archive_handle->offset += blk_sz; + + /* prevent bb_strtou from running off the buffer */ + buf[sz] = '\0'; + + while (sz != 0) { + char *end, *value; + unsigned len; + + /* Every record has this format: "LEN NAME=VALUE\n" */ + len = bb_strtou(p, &end, 10); + /* expect errno to be EINVAL, because the character + * following the digits should be a space + */ + p += len; + sz -= len; + 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 != ' ' + ) { + bb_error_msg("malformed extended header, skipped"); + // More verbose version: + //bb_error_msg("malformed extended header at %"OFF_FMT"d, skipped", + // archive_handle->offset - (sz + len)); + break; + } + /* overwrite the terminating newline with NUL + * (we do not bother to check that it *was* a newline) + */ + p[-1] = '\0'; + value = end + 1; + +# 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 + +# 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 (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 + } + + free(buf); +#endif +} + +char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) +{ + file_header_t *file_header = archive_handle->file_header; + struct tar_header_t tar; + char *cp; + int tar_typeflag; /* can be "char", "int" seems give smaller code */ + int i, sum_u, sum; +#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY + int sum_s; +#endif + int parse_names; + + /* Our "private data" */ +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS +# define p_longname (archive_handle->tar__longname) +# define p_linkname (archive_handle->tar__linkname) +#else +# define p_longname 0 +# define p_linkname 0 +#endif + +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS || ENABLE_FEATURE_TAR_SELINUX + again: +#endif + /* Align header */ + data_align(archive_handle, 512); + + again_after_align: + +#if ENABLE_DESKTOP || ENABLE_FEATURE_TAR_AUTODETECT + /* to prevent misdetection of bz2 sig */ + *(aliased_uint32_t*)&tar = 0; + i = full_read(archive_handle->src_fd, &tar, 512); + /* If GNU tar sees EOF in above read, it says: + * "tar: A lone zero block at N", where N = kilobyte + * where EOF was met (not EOF block, actual EOF!), + * and exits with EXIT_SUCCESS. + * We will mimic exit(EXIT_SUCCESS), although we will not mimic + * the message and we don't check whether we indeed + * saw zero block directly before this. */ + if (i == 0) { + /* 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; + } + if (i != 512) { + IF_FEATURE_TAR_AUTODETECT(goto autodetect;) + bb_error_msg_and_die("short read"); + } + +#else + i = 512; + xread(archive_handle->src_fd, &tar, i); +#endif + archive_handle->offset += i; + + /* If there is no filename its an empty header */ + 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 + */ + while (full_read(archive_handle->src_fd, &tar, 512) == 512) + continue; + return EXIT_FAILURE; /* "end of archive" */ + } + archive_handle->tar__end = 1; + return EXIT_SUCCESS; /* "decoded one header" */ + } + archive_handle->tar__end = 0; + + /* Check header has valid magic, "ustar" is for the proper tar, + * five NULs are for the old tar format */ + if (!is_prefixed_with(tar.magic, "ustar") + && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY + || memcmp(tar.magic, "\0\0\0\0", 5) != 0) + ) { +#if ENABLE_FEATURE_TAR_AUTODETECT + autodetect: + /* Two different causes for lseek() != 0: + * unseekable fd (would like to support that too, but...), + * or not first block (false positive, it's not .gz/.bz2!) */ + if (lseek(archive_handle->src_fd, -i, SEEK_CUR) != 0) + goto err; + if (setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_compressed:*/ 0) != 0) + err: + bb_error_msg_and_die("invalid tar magic"); + archive_handle->offset = 0; + goto again_after_align; +#endif + bb_error_msg_and_die("invalid tar magic"); + } + + /* Do checksum on headers. + * POSIX says that checksum is done on unsigned bytes, but + * Sun and HP-UX gets it wrong... more details in + * GNU tar source. */ + sum_u = ' ' * sizeof(tar.chksum); +#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY + sum_s = sum_u; +#endif + for (i = 0; i < 148; i++) { + sum_u += ((unsigned char*)&tar)[i]; +#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY + sum_s += ((signed char*)&tar)[i]; +#endif + } + for (i = 156; i < 512; i++) { + sum_u += ((unsigned char*)&tar)[i]; +#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY + sum_s += ((signed char*)&tar)[i]; +#endif + } + /* Most tarfiles have tar.chksum NUL or space terminated, but + * github.com decided to be "special" and have unterminated field: + * 0090: 30343300 30303031 33323731 30000000 |043.000132710...| + * ^^^^^^^^| + * Need to use GET_OCTAL. This overwrites tar.typeflag ---+ + * (the '0' char immediately after chksum in example above) with NUL. + */ + tar_typeflag = (uint8_t)tar.typeflag; /* save it */ + sum = GET_OCTAL(tar.chksum); + if (sum_u != sum + IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum) + ) { + bb_error_msg_and_die("invalid tar header checksum"); + } + + /* GET_OCTAL trashes subsequent field, therefore we call it + * on fields in reverse order */ + if (tar.devmajor[0]) { + char t = tar.prefix[0]; + /* we trash prefix[0] here, but we DO need it later! */ + unsigned minor = GET_OCTAL(tar.devminor); + unsigned major = GET_OCTAL(tar.devmajor); + file_header->device = makedev(major, minor); + tar.prefix[0] = t; + } + + /* 0 is reserved for high perf file, treat as normal file */ + if (tar_typeflag == '\0') tar_typeflag = '0'; + parse_names = (tar_typeflag >= '0' && tar_typeflag <= '7'); + + file_header->link_target = NULL; + if (!p_linkname && parse_names && tar.linkname[0]) { + file_header->link_target = xstrndup(tar.linkname, sizeof(tar.linkname)); + /* FIXME: what if we have non-link object with link_target? */ + /* Will link_target be free()ed? */ + } +#if ENABLE_FEATURE_TAR_UNAME_GNAME + file_header->tar__uname = tar.uname[0] ? xstrndup(tar.uname, sizeof(tar.uname)) : NULL; + file_header->tar__gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL; +#endif + file_header->mtime = GET_OCTAL(tar.mtime); + file_header->size = GET_OCTAL(tar.size); + file_header->gid = GET_OCTAL(tar.gid); + file_header->uid = GET_OCTAL(tar.uid); + /* Set bits 0-11 of the files mode */ + file_header->mode = 07777 & GET_OCTAL(tar.mode); + + file_header->name = NULL; + if (!p_longname && parse_names) { + /* we trash mode[0] here, it's ok */ + //tar.name[sizeof(tar.name)] = '\0'; - gcc 4.3.0 would complain + tar.mode[0] = '\0'; + if (tar.prefix[0]) { + /* and padding[0] */ + //tar.prefix[sizeof(tar.prefix)] = '\0'; - gcc 4.3.0 would complain + tar.padding[0] = '\0'; + file_header->name = concat_path_file(tar.prefix, tar.name); + } else + file_header->name = xstrdup(tar.name); + } + + /* Set bits 12-15 of the files mode */ + /* (typeflag was not trashed because chksum does not use getOctal) */ + switch (tar_typeflag) { + case '1': /* hardlink */ + /* we mark hardlinks as regular files with zero size and a link name */ + file_header->mode |= S_IFREG; + /* on size of link fields from star(4) + * ... For tar archives written by pre POSIX.1-1988 + * implementations, the size field usually contains the size of + * the file and needs to be ignored as no data may follow this + * header type. For POSIX.1- 1988 compliant archives, the size + * field needs to be 0. For POSIX.1-2001 compliant archives, + * the size field may be non zero, indicating that file data is + * included in the archive. + * i.e; always assume this is zero for safety. + */ + goto size0; + case '7': + /* case 0: */ + case '0': +#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY + if (last_char_is(file_header->name, '/')) { + goto set_dir; + } +#endif + file_header->mode |= S_IFREG; + break; + case '2': + file_header->mode |= S_IFLNK; + /* have seen tarballs with size field containing + * the size of the link target's name */ + size0: + file_header->size = 0; + break; + case '3': + file_header->mode |= S_IFCHR; + goto size0; /* paranoia */ + case '4': + file_header->mode |= S_IFBLK; + goto size0; + case '5': + IF_FEATURE_TAR_OLDGNU_COMPATIBILITY(set_dir:) + file_header->mode |= S_IFDIR; + goto size0; + 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); + /* For paranoia reasons we allocate extra NUL char */ + p_longname = xzalloc(file_header->size + 1); + /* We read ASCIZ string, including NUL */ + xread(archive_handle->src_fd, p_longname, file_header->size); + archive_handle->offset += file_header->size; + /* return get_header_tar(archive_handle); */ + /* gcc 4.1.1 didn't optimize it into jump */ + /* so we will do it ourself, this also saves stack */ + goto again; + case 'K': + free(p_linkname); + p_linkname = xzalloc(file_header->size + 1); + xread(archive_handle->src_fd, p_linkname, file_header->size); + archive_handle->offset += file_header->size; + /* return get_header_tar(archive_handle); */ + goto again; +/* + * 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 + } + skip_ext_hdr: + { + off_t sz; + bb_error_msg("warning: skipping header '%c'", tar_typeflag); + sz = (file_header->size + 511) & ~(off_t)511; + archive_handle->offset += sz; + sz >>= 9; /* sz /= 512 but w/o contortions for signed div */ + while (sz--) + xread(archive_handle->src_fd, &tar, 512); + /* return get_header_tar(archive_handle); */ + goto again_after_align; + } + default: + bb_error_msg_and_die("unknown typeflag: 0x%x", tar_typeflag); + } + +#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS + if (p_longname) { + file_header->name = p_longname; + p_longname = NULL; + } + if (p_linkname) { + file_header->link_target = p_linkname; + p_linkname = NULL; + } +#endif + + /* 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 */ + cp = last_char_is(file_header->name, '/'); + + if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { + archive_handle->action_header(/*archive_handle->*/ file_header); + /* Note that we kill the '/' only after action_header() */ + /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */ + if (cp) + *cp = '\0'; + archive_handle->action_data(archive_handle); + if (archive_handle->accept || archive_handle->reject + || (archive_handle->ah_flags & ARCHIVE_REMEMBER_NAMES) + ) { + llist_add_to(&archive_handle->passed, file_header->name); + } else /* Caller isn't interested in list of unpacked files */ + free(file_header->name); + } else { + data_skip(archive_handle); + free(file_header->name); + } + archive_handle->offset += file_header->size; + + free(file_header->link_target); + /* Do not free(file_header->name)! + * It might be inserted in archive_handle->passed - see above */ +#if ENABLE_FEATURE_TAR_UNAME_GNAME + free(file_header->tar__uname); + free(file_header->tar__gname); +#endif + return EXIT_SUCCESS; /* "decoded one header" */ +} diff --git a/busybox/archival/libarchive/get_header_tar_bz2.c b/busybox/archival/libarchive/get_header_tar_bz2.c new file mode 100644 index 00000000000000..c021720aca8b38 --- /dev/null +++ b/busybox/archival/libarchive/get_header_tar_bz2.c @@ -0,0 +1,20 @@ +/* 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_bz2(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_bz2_stream, "bunzip2"); + 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/busybox/archival/libarchive/get_header_tar_gz.c b/busybox/archival/libarchive/get_header_tar_gz.c new file mode 100644 index 00000000000000..793b161a16cb41 --- /dev/null +++ b/busybox/archival/libarchive/get_header_tar_gz.c @@ -0,0 +1,20 @@ +/* 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_gz(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_gz_stream, "gunzip"); + 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/busybox/archival/libarchive/get_header_tar_lzma.c b/busybox/archival/libarchive/get_header_tar_lzma.c new file mode 100644 index 00000000000000..15d10adb81b6ba --- /dev/null +++ b/busybox/archival/libarchive/get_header_tar_lzma.c @@ -0,0 +1,23 @@ +/* vi: set sw=4 ts=4: */ +/* + * Small lzma deflate implementation. + * Copyright (C) 2006 Aurelien Jacobs + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include "libbb.h" +#include "bb_archive.h" + +char FAST_FUNC get_header_tar_lzma(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_lzma_stream, "unlzma"); + 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/busybox/archival/libarchive/get_header_tar_xz.c b/busybox/archival/libarchive/get_header_tar_xz.c new file mode 100644 index 00000000000000..852c989cec2555 --- /dev/null +++ b/busybox/archival/libarchive/get_header_tar_xz.c @@ -0,0 +1,20 @@ +/* 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/busybox/archival/libarchive/header_list.c b/busybox/archival/libarchive/header_list.c new file mode 100644 index 00000000000000..0621aa406a0332 --- /dev/null +++ b/busybox/archival/libarchive/header_list.c @@ -0,0 +1,12 @@ +/* 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" + +void FAST_FUNC header_list(const file_header_t *file_header) +{ +//TODO: cpio -vp DIR should output "DIR/NAME", not just "NAME" */ + puts(file_header->name); +} diff --git a/busybox/archival/libarchive/header_skip.c b/busybox/archival/libarchive/header_skip.c new file mode 100644 index 00000000000000..f5987bfe275432 --- /dev/null +++ b/busybox/archival/libarchive/header_skip.c @@ -0,0 +1,10 @@ +/* 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" + +void FAST_FUNC header_skip(const file_header_t *file_header UNUSED_PARAM) +{ +} diff --git a/busybox/archival/libarchive/header_verbose_list.c b/busybox/archival/libarchive/header_verbose_list.c new file mode 100644 index 00000000000000..be5140f8bdf268 --- /dev/null +++ b/busybox/archival/libarchive/header_verbose_list.c @@ -0,0 +1,68 @@ +/* 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" + +void FAST_FUNC header_verbose_list(const file_header_t *file_header) +{ + struct tm tm_time; + struct tm *ptm = &tm_time; //localtime(&file_header->mtime); + +#if ENABLE_FEATURE_TAR_UNAME_GNAME + char uid[sizeof(int)*3 + 2]; + /*char gid[sizeof(int)*3 + 2];*/ + char *user; + char *group; + + localtime_r(&file_header->mtime, ptm); + + user = file_header->tar__uname; + if (user == NULL) { + sprintf(uid, "%u", (unsigned)file_header->uid); + user = uid; + } + group = file_header->tar__gname; + if (group == NULL) { + /*sprintf(gid, "%u", (unsigned)file_header->gid);*/ + group = utoa(file_header->gid); + } + printf("%s %s/%s %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s", + bb_mode_string(file_header->mode), + user, + group, + file_header->size, + 1900 + ptm->tm_year, + 1 + ptm->tm_mon, + ptm->tm_mday, + ptm->tm_hour, + ptm->tm_min, + ptm->tm_sec, + file_header->name); + +#else /* !FEATURE_TAR_UNAME_GNAME */ + + localtime_r(&file_header->mtime, ptm); + + printf("%s %u/%u %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s", + bb_mode_string(file_header->mode), + (unsigned)file_header->uid, + (unsigned)file_header->gid, + file_header->size, + 1900 + ptm->tm_year, + 1 + ptm->tm_mon, + ptm->tm_mday, + ptm->tm_hour, + ptm->tm_min, + ptm->tm_sec, + file_header->name); + +#endif /* FEATURE_TAR_UNAME_GNAME */ + + /* NB: GNU tar shows "->" for symlinks and "link to" for hardlinks */ + if (file_header->link_target) { + printf(" -> %s", file_header->link_target); + } + bb_putchar('\n'); +} diff --git a/busybox/archival/libarchive/init_handle.c b/busybox/archival/libarchive/init_handle.c new file mode 100644 index 00000000000000..4c64dac585415c --- /dev/null +++ b/busybox/archival/libarchive/init_handle.c @@ -0,0 +1,25 @@ +/* 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" + +archive_handle_t* FAST_FUNC init_handle(void) +{ + archive_handle_t *archive_handle; + + /* Initialize default values */ + archive_handle = xzalloc(sizeof(archive_handle_t)); + archive_handle->file_header = xzalloc(sizeof(file_header_t)); + archive_handle->action_header = header_skip; + 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/busybox/archival/libarchive/liblzo.h b/busybox/archival/libarchive/liblzo.h new file mode 100644 index 00000000000000..4596620fee23e6 --- /dev/null +++ b/busybox/archival/libarchive/liblzo.h @@ -0,0 +1,95 @@ +/* + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzo/ + + The LZO library is free software; you can 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "liblzo_interface.h" + +/* lzo-2.03/src/config1x.h */ +#define M2_MIN_LEN 3 +#define M2_MAX_LEN 8 +#define M3_MAX_LEN 33 +#define M4_MAX_LEN 9 +#define M1_MAX_OFFSET 0x0400 +#define M2_MAX_OFFSET 0x0800 +#define M3_MAX_OFFSET 0x4000 +#define M4_MAX_OFFSET 0xbfff +#define M1_MARKER 0 +#define M3_MARKER 32 +#define M4_MARKER 16 + +#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET) +#define MIN_LOOKAHEAD (M2_MAX_LEN + 1) + +#define LZO_EOF_CODE + +/* lzo-2.03/src/lzo_dict.h */ +#define GINDEX(m_pos,m_off,dict,dindex,in) m_pos = dict[dindex] +#define DX2(p,s1,s2) \ + (((((unsigned)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0]) +//#define DA3(p,s1,s2,s3) ((DA2((p)+1,s2,s3) << (s1)) + (p)[0]) +//#define DS3(p,s1,s2,s3) ((DS2((p)+1,s2,s3) << (s1)) - (p)[0]) +#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0]) + +#define D_SIZE (1U << D_BITS) +#define D_MASK ((1U << D_BITS) - 1) +#define D_HIGH ((D_MASK >> 1) + 1) + +#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ + ( \ + m_pos = ip - (unsigned)(ip - m_pos), \ + ((uintptr_t)m_pos < (uintptr_t)in \ + || (m_off = (unsigned)(ip - m_pos)) <= 0 \ + || m_off > max_offset) \ + ) + +#define DENTRY(p,in) (p) +#define UPDATE_I(dict,drun,index,p,in) dict[index] = DENTRY(p,in) + +#define DMS(v,s) ((unsigned) (((v) & (D_MASK >> (s))) << (s))) +#define DM(v) ((unsigned) ((v) & D_MASK)) +#define DMUL(a,b) ((unsigned) ((a) * (b))) + +/* lzo-2.03/src/lzo_ptr.h */ +#define pd(a,b) ((unsigned)((a)-(b))) + +# define TEST_IP (ip < ip_end) +# define NEED_IP(x) \ + if ((unsigned)(ip_end - ip) < (unsigned)(x)) goto input_overrun +# define TEST_IV(x) if ((x) > (unsigned)0 - (511)) goto input_overrun + +# undef TEST_OP /* don't need both of the tests here */ +# define TEST_OP 1 +# define NEED_OP(x) \ + if ((unsigned)(op_end - op) < (unsigned)(x)) goto output_overrun +# define TEST_OV(x) if ((x) > (unsigned)0 - (511)) goto output_overrun + +#define HAVE_ANY_OP 1 + +//#if defined(LZO_TEST_OVERRUN_LOOKBEHIND) +# define TEST_LB(m_pos) if (m_pos < out || m_pos >= op) goto lookbehind_overrun +//# define TEST_LBO(m_pos,o) if (m_pos < out || m_pos >= op - (o)) goto lookbehind_overrun +//#else +//# define TEST_LB(m_pos) ((void) 0) +//# define TEST_LBO(m_pos,o) ((void) 0) +//#endif diff --git a/busybox/archival/libarchive/lzo1x_1.c b/busybox/archival/libarchive/lzo1x_1.c new file mode 100644 index 00000000000000..a8883984681683 --- /dev/null +++ b/busybox/archival/libarchive/lzo1x_1.c @@ -0,0 +1,35 @@ +/* LZO1X-1 compression + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzo/ + + The LZO library is free software; you can 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "libbb.h" +#include "liblzo.h" + +#define D_BITS 14 +#define D_INDEX1(d,p) d = DM(DMUL(0x21,DX3(p,5,5,6)) >> 5) +#define D_INDEX2(d,p) d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f) + +#define DO_COMPRESS lzo1x_1_compress + +#include "lzo1x_c.c" diff --git a/busybox/archival/libarchive/lzo1x_1o.c b/busybox/archival/libarchive/lzo1x_1o.c new file mode 100644 index 00000000000000..3c61253e0edf8c --- /dev/null +++ b/busybox/archival/libarchive/lzo1x_1o.c @@ -0,0 +1,35 @@ +/* LZO1X-1(15) compression + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzo/ + + The LZO library is free software; you can 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "libbb.h" +#include "liblzo.h" + +#define D_BITS 15 +#define D_INDEX1(d,p) d = DM(DMUL(0x21,DX3(p,5,5,6)) >> 5) +#define D_INDEX2(d,p) d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f) + +#define DO_COMPRESS lzo1x_1_15_compress + +#include "lzo1x_c.c" diff --git a/busybox/archival/libarchive/lzo1x_9x.c b/busybox/archival/libarchive/lzo1x_9x.c new file mode 100644 index 00000000000000..09ee4ba5c3b168 --- /dev/null +++ b/busybox/archival/libarchive/lzo1x_9x.c @@ -0,0 +1,919 @@ +/* lzo1x_9x.c -- implementation of the LZO1X-999 compression algorithm + + This file is part of the LZO real-time data compression library. + + Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library is free software; you can 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Markus F.X.J. Oberhumer + + http://www.oberhumer.com/opensource/lzo/ +*/ +#include "libbb.h" + +/* The following is probably only safe on Intel-compatible processors ... */ +#define LZO_UNALIGNED_OK_2 +#define LZO_UNALIGNED_OK_4 + +#include "liblzo.h" + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +#define LZO_MIN(a,b) ((a) <= (b) ? (a) : (b)) +#define LZO_MAX3(a,b,c) ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c)) + +/*********************************************************************** +// +************************************************************************/ +#define SWD_N M4_MAX_OFFSET /* size of ring buffer */ +#define SWD_F 2048 /* upper limit for match length */ + +#define SWD_BEST_OFF (LZO_MAX3(M2_MAX_LEN, M3_MAX_LEN, M4_MAX_LEN) + 1) + +typedef struct { + int init; + + unsigned look; /* bytes in lookahead buffer */ + + unsigned m_len; + unsigned m_off; + + const uint8_t *bp; + const uint8_t *ip; + const uint8_t *in; + const uint8_t *in_end; + uint8_t *out; + + unsigned r1_lit; +} lzo1x_999_t; + +#define getbyte(c) ((c).ip < (c).in_end ? *((c).ip)++ : (-1)) + +/* lzo_swd.c -- sliding window dictionary */ + +/*********************************************************************** +// +************************************************************************/ +#define SWD_UINT_MAX USHRT_MAX + +#ifndef SWD_HSIZE +# define SWD_HSIZE 16384 +#endif +#ifndef SWD_MAX_CHAIN +# define SWD_MAX_CHAIN 2048 +#endif + +#define HEAD3(b, p) \ + ( ((0x9f5f * ((((b[p]<<5)^b[p+1])<<5) ^ b[p+2])) >> 5) & (SWD_HSIZE-1) ) + +#if defined(LZO_UNALIGNED_OK_2) +# define HEAD2(b,p) (* (bb__aliased_uint16_t *) &(b[p])) +#else +# define HEAD2(b,p) (b[p] ^ ((unsigned)b[p+1]<<8)) +#endif +#define NIL2 SWD_UINT_MAX + +typedef struct lzo_swd { + /* public - "built-in" */ + + /* public - configuration */ + unsigned max_chain; + int use_best_off; + + /* public - output */ + unsigned m_len; + unsigned m_off; + unsigned look; + int b_char; +#if defined(SWD_BEST_OFF) + unsigned best_off[SWD_BEST_OFF]; +#endif + + /* semi public */ + lzo1x_999_t *c; + unsigned m_pos; +#if defined(SWD_BEST_OFF) + unsigned best_pos[SWD_BEST_OFF]; +#endif + + /* private */ + unsigned ip; /* input pointer (lookahead) */ + unsigned bp; /* buffer pointer */ + unsigned rp; /* remove pointer */ + + unsigned node_count; + unsigned first_rp; + + uint8_t b[SWD_N + SWD_F]; + uint8_t b_wrap[SWD_F]; /* must follow b */ + uint16_t head3[SWD_HSIZE]; + uint16_t succ3[SWD_N + SWD_F]; + uint16_t best3[SWD_N + SWD_F]; + uint16_t llen3[SWD_HSIZE]; +#ifdef HEAD2 + uint16_t head2[65536L]; +#endif +} lzo_swd_t, *lzo_swd_p; + +#define SIZEOF_LZO_SWD_T (sizeof(lzo_swd_t)) + + +/* Access macro for head3. + * head3[key] may be uninitialized, but then its value will never be used. + */ +#define s_get_head3(s,key) s->head3[key] + + +/*********************************************************************** +// +************************************************************************/ +#define B_SIZE (SWD_N + SWD_F) + +static int swd_init(lzo_swd_p s) +{ + /* defaults */ + s->node_count = SWD_N; + + memset(s->llen3, 0, sizeof(s->llen3[0]) * (unsigned)SWD_HSIZE); +#ifdef HEAD2 + memset(s->head2, 0xff, sizeof(s->head2[0]) * 65536L); + assert(s->head2[0] == NIL2); +#endif + + s->ip = 0; + s->bp = s->ip; + s->first_rp = s->ip; + + assert(s->ip + SWD_F <= B_SIZE); + s->look = (unsigned) (s->c->in_end - s->c->ip); + if (s->look > 0) { + if (s->look > SWD_F) + s->look = SWD_F; + memcpy(&s->b[s->ip], s->c->ip, s->look); + s->c->ip += s->look; + s->ip += s->look; + } + if (s->ip == B_SIZE) + s->ip = 0; + + s->rp = s->first_rp; + if (s->rp >= s->node_count) + s->rp -= s->node_count; + else + s->rp += B_SIZE - s->node_count; + + return LZO_E_OK; +} + +#define swd_pos2off(s,pos) \ + (s->bp > (pos) ? s->bp - (pos) : B_SIZE - ((pos) - s->bp)) + + +/*********************************************************************** +// +************************************************************************/ +static void swd_getbyte(lzo_swd_p s) +{ + int c; + + if ((c = getbyte(*(s->c))) < 0) { + if (s->look > 0) + --s->look; + } else { + s->b[s->ip] = c; + if (s->ip < SWD_F) + s->b_wrap[s->ip] = c; + } + if (++s->ip == B_SIZE) + s->ip = 0; + if (++s->bp == B_SIZE) + s->bp = 0; + if (++s->rp == B_SIZE) + s->rp = 0; +} + + +/*********************************************************************** +// remove node from lists +************************************************************************/ +static void swd_remove_node(lzo_swd_p s, unsigned node) +{ + if (s->node_count == 0) { + unsigned key; + + key = HEAD3(s->b,node); + assert(s->llen3[key] > 0); + --s->llen3[key]; + +#ifdef HEAD2 + key = HEAD2(s->b,node); + assert(s->head2[key] != NIL2); + if ((unsigned) s->head2[key] == node) + s->head2[key] = NIL2; +#endif + } else + --s->node_count; +} + + +/*********************************************************************** +// +************************************************************************/ +static void swd_accept(lzo_swd_p s, unsigned n) +{ + assert(n <= s->look); + + while (n--) { + unsigned key; + + swd_remove_node(s,s->rp); + + /* add bp into HEAD3 */ + key = HEAD3(s->b, s->bp); + s->succ3[s->bp] = s_get_head3(s, key); + s->head3[key] = s->bp; + s->best3[s->bp] = SWD_F + 1; + s->llen3[key]++; + assert(s->llen3[key] <= SWD_N); + +#ifdef HEAD2 + /* add bp into HEAD2 */ + key = HEAD2(s->b, s->bp); + s->head2[key] = s->bp; +#endif + + swd_getbyte(s); + } +} + + +/*********************************************************************** +// +************************************************************************/ +static void swd_search(lzo_swd_p s, unsigned node, unsigned cnt) +{ + const uint8_t *p1; + const uint8_t *p2; + const uint8_t *px; + unsigned m_len = s->m_len; + const uint8_t *b = s->b; + const uint8_t *bp = s->b + s->bp; + const uint8_t *bx = s->b + s->bp + s->look; + unsigned char scan_end1; + + assert(s->m_len > 0); + + scan_end1 = bp[m_len - 1]; + for ( ; cnt-- > 0; node = s->succ3[node]) { + p1 = bp; + p2 = b + node; + px = bx; + + assert(m_len < s->look); + + if (p2[m_len - 1] == scan_end1 + && p2[m_len] == p1[m_len] + && p2[0] == p1[0] + && p2[1] == p1[1] + ) { + unsigned i; + assert(lzo_memcmp(bp, &b[node], 3) == 0); + + p1 += 2; p2 += 2; + do {} while (++p1 < px && *p1 == *++p2); + i = p1-bp; + + assert(lzo_memcmp(bp, &b[node], i) == 0); + +#if defined(SWD_BEST_OFF) + if (i < SWD_BEST_OFF) { + if (s->best_pos[i] == 0) + s->best_pos[i] = node + 1; + } +#endif + if (i > m_len) { + s->m_len = m_len = i; + s->m_pos = node; + if (m_len == s->look) + return; + if (m_len >= SWD_F) + return; + if (m_len > (unsigned) s->best3[node]) + return; + scan_end1 = bp[m_len - 1]; + } + } + } +} + + +/*********************************************************************** +// +************************************************************************/ +#ifdef HEAD2 + +static int swd_search2(lzo_swd_p s) +{ + unsigned key; + + assert(s->look >= 2); + assert(s->m_len > 0); + + key = s->head2[HEAD2(s->b, s->bp)]; + if (key == NIL2) + return 0; + assert(lzo_memcmp(&s->b[s->bp], &s->b[key], 2) == 0); +#if defined(SWD_BEST_OFF) + if (s->best_pos[2] == 0) + s->best_pos[2] = key + 1; +#endif + + if (s->m_len < 2) { + s->m_len = 2; + s->m_pos = key; + } + return 1; +} + +#endif + + +/*********************************************************************** +// +************************************************************************/ +static void swd_findbest(lzo_swd_p s) +{ + unsigned key; + unsigned cnt, node; + unsigned len; + + assert(s->m_len > 0); + + /* get current head, add bp into HEAD3 */ + key = HEAD3(s->b,s->bp); + node = s->succ3[s->bp] = s_get_head3(s, key); + cnt = s->llen3[key]++; + assert(s->llen3[key] <= SWD_N + SWD_F); + if (cnt > s->max_chain) + cnt = s->max_chain; + s->head3[key] = s->bp; + + s->b_char = s->b[s->bp]; + len = s->m_len; + if (s->m_len >= s->look) { + if (s->look == 0) + s->b_char = -1; + s->m_off = 0; + s->best3[s->bp] = SWD_F + 1; + } else { +#ifdef HEAD2 + if (swd_search2(s)) +#endif + if (s->look >= 3) + swd_search(s, node, cnt); + if (s->m_len > len) + s->m_off = swd_pos2off(s,s->m_pos); + s->best3[s->bp] = s->m_len; + +#if defined(SWD_BEST_OFF) + if (s->use_best_off) { + int i; + for (i = 2; i < SWD_BEST_OFF; i++) { + if (s->best_pos[i] > 0) + s->best_off[i] = swd_pos2off(s, s->best_pos[i]-1); + else + s->best_off[i] = 0; + } + } +#endif + } + + swd_remove_node(s,s->rp); + +#ifdef HEAD2 + /* add bp into HEAD2 */ + key = HEAD2(s->b, s->bp); + s->head2[key] = s->bp; +#endif +} + +#undef HEAD3 +#undef HEAD2 +#undef s_get_head3 + + +/*********************************************************************** +// +************************************************************************/ +static int init_match(lzo1x_999_t *c, lzo_swd_p s, uint32_t use_best_off) +{ + int r; + + assert(!c->init); + c->init = 1; + + s->c = c; + + r = swd_init(s); + if (r != 0) + return r; + + s->use_best_off = use_best_off; + return r; +} + + +/*********************************************************************** +// +************************************************************************/ +static int find_match(lzo1x_999_t *c, lzo_swd_p s, + unsigned this_len, unsigned skip) +{ + assert(c->init); + + if (skip > 0) { + assert(this_len >= skip); + swd_accept(s, this_len - skip); + } else { + assert(this_len <= 1); + } + + s->m_len = 1; +#ifdef SWD_BEST_OFF + if (s->use_best_off) + memset(s->best_pos, 0, sizeof(s->best_pos)); +#endif + swd_findbest(s); + c->m_len = s->m_len; + c->m_off = s->m_off; + + swd_getbyte(s); + + if (s->b_char < 0) { + c->look = 0; + c->m_len = 0; + } else { + c->look = s->look + 1; + } + c->bp = c->ip - c->look; + + return LZO_E_OK; +} + +/* this is a public functions, but there is no prototype in a header file */ +static int lzo1x_999_compress_internal(const uint8_t *in , unsigned in_len, + uint8_t *out, unsigned *out_len, + void *wrkmem, + unsigned good_length, + unsigned max_lazy, + unsigned max_chain, + uint32_t use_best_off); + + +/*********************************************************************** +// +************************************************************************/ +static uint8_t *code_match(lzo1x_999_t *c, + uint8_t *op, unsigned m_len, unsigned m_off) +{ + assert(op > c->out); + if (m_len == 2) { + assert(m_off <= M1_MAX_OFFSET); + assert(c->r1_lit > 0); + assert(c->r1_lit < 4); + m_off -= 1; + *op++ = M1_MARKER | ((m_off & 3) << 2); + *op++ = m_off >> 2; + } else if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) { + assert(m_len >= 3); + m_off -= 1; + *op++ = ((m_len - 1) << 5) | ((m_off & 7) << 2); + *op++ = m_off >> 3; + assert(op[-2] >= M2_MARKER); + } else if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET && c->r1_lit >= 4) { + assert(m_len == 3); + assert(m_off > M2_MAX_OFFSET); + m_off -= 1 + M2_MAX_OFFSET; + *op++ = M1_MARKER | ((m_off & 3) << 2); + *op++ = m_off >> 2; + } else if (m_off <= M3_MAX_OFFSET) { + assert(m_len >= 3); + m_off -= 1; + if (m_len <= M3_MAX_LEN) + *op++ = M3_MARKER | (m_len - 2); + else { + m_len -= M3_MAX_LEN; + *op++ = M3_MARKER | 0; + while (m_len > 255) { + m_len -= 255; + *op++ = 0; + } + assert(m_len > 0); + *op++ = m_len; + } + *op++ = m_off << 2; + *op++ = m_off >> 6; + } else { + unsigned k; + + assert(m_len >= 3); + assert(m_off > 0x4000); + assert(m_off <= 0xbfff); + m_off -= 0x4000; + k = (m_off & 0x4000) >> 11; + if (m_len <= M4_MAX_LEN) + *op++ = M4_MARKER | k | (m_len - 2); + else { + m_len -= M4_MAX_LEN; + *op++ = M4_MARKER | k | 0; + while (m_len > 255) { + m_len -= 255; + *op++ = 0; + } + assert(m_len > 0); + *op++ = m_len; + } + *op++ = m_off << 2; + *op++ = m_off >> 6; + } + + return op; +} + + +static uint8_t *STORE_RUN(lzo1x_999_t *c, uint8_t *op, + const uint8_t *ii, unsigned t) +{ + if (op == c->out && t <= 238) { + *op++ = 17 + t; + } else if (t <= 3) { + op[-2] |= t; + } else if (t <= 18) { + *op++ = t - 3; + } else { + unsigned tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = tt; + } + do *op++ = *ii++; while (--t > 0); + + return op; +} + + +static uint8_t *code_run(lzo1x_999_t *c, uint8_t *op, const uint8_t *ii, + unsigned lit) +{ + if (lit > 0) { + assert(m_len >= 2); + op = STORE_RUN(c, op, ii, lit); + } else { + assert(m_len >= 3); + } + c->r1_lit = lit; + + return op; +} + + +/*********************************************************************** +// +************************************************************************/ +static int len_of_coded_match(unsigned m_len, unsigned m_off, unsigned lit) +{ + int n = 4; + + if (m_len < 2) + return -1; + if (m_len == 2) + return (m_off <= M1_MAX_OFFSET && lit > 0 && lit < 4) ? 2 : -1; + if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) + return 2; + if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET && lit >= 4) + return 2; + if (m_off <= M3_MAX_OFFSET) { + if (m_len <= M3_MAX_LEN) + return 3; + m_len -= M3_MAX_LEN; + } else if (m_off <= M4_MAX_OFFSET) { + if (m_len <= M4_MAX_LEN) + return 3; + m_len -= M4_MAX_LEN; + } else + return -1; + while (m_len > 255) { + m_len -= 255; + n++; + } + return n; +} + + +static int min_gain(unsigned ahead, unsigned lit1, + unsigned lit2, int l1, int l2, int l3) +{ + int lazy_match_min_gain = 0; + + assert (ahead >= 1); + lazy_match_min_gain += ahead; + + if (lit1 <= 3) + lazy_match_min_gain += (lit2 <= 3) ? 0 : 2; + else if (lit1 <= 18) + lazy_match_min_gain += (lit2 <= 18) ? 0 : 1; + + lazy_match_min_gain += (l2 - l1) * 2; + if (l3 > 0) + lazy_match_min_gain -= (ahead - l3) * 2; + + if (lazy_match_min_gain < 0) + lazy_match_min_gain = 0; + + return lazy_match_min_gain; +} + + +/*********************************************************************** +// +************************************************************************/ +#if defined(SWD_BEST_OFF) + +static void better_match(const lzo_swd_p swd, + unsigned *m_len, unsigned *m_off) +{ + if (*m_len <= M2_MIN_LEN) + return; + + if (*m_off <= M2_MAX_OFFSET) + return; + + /* M3/M4 -> M2 */ + if (*m_off > M2_MAX_OFFSET + && *m_len >= M2_MIN_LEN + 1 && *m_len <= M2_MAX_LEN + 1 + && swd->best_off[*m_len-1] && swd->best_off[*m_len-1] <= M2_MAX_OFFSET + ) { + *m_len = *m_len - 1; + *m_off = swd->best_off[*m_len]; + return; + } + + /* M4 -> M2 */ + if (*m_off > M3_MAX_OFFSET + && *m_len >= M4_MAX_LEN + 1 && *m_len <= M2_MAX_LEN + 2 + && swd->best_off[*m_len-2] && swd->best_off[*m_len-2] <= M2_MAX_OFFSET + ) { + *m_len = *m_len - 2; + *m_off = swd->best_off[*m_len]; + return; + } + /* M4 -> M3 */ + if (*m_off > M3_MAX_OFFSET + && *m_len >= M4_MAX_LEN + 1 && *m_len <= M3_MAX_LEN + 1 + && swd->best_off[*m_len-1] && swd->best_off[*m_len-1] <= M3_MAX_OFFSET + ) { + *m_len = *m_len - 1; + *m_off = swd->best_off[*m_len]; + } +} + +#endif + + +/*********************************************************************** +// +************************************************************************/ +static int lzo1x_999_compress_internal(const uint8_t *in, unsigned in_len, + uint8_t *out, unsigned *out_len, + void *wrkmem, + unsigned good_length, + unsigned max_lazy, + unsigned max_chain, + uint32_t use_best_off) +{ + uint8_t *op; + const uint8_t *ii; + unsigned lit; + unsigned m_len, m_off; + lzo1x_999_t cc; + lzo1x_999_t *const c = &cc; + const lzo_swd_p swd = (lzo_swd_p) wrkmem; + int r; + + c->init = 0; + c->ip = c->in = in; + c->in_end = in + in_len; + c->out = out; + + op = out; + ii = c->ip; /* point to start of literal run */ + lit = 0; + c->r1_lit = 0; + + r = init_match(c, swd, use_best_off); + if (r != 0) + return r; + swd->max_chain = max_chain; + + r = find_match(c, swd, 0, 0); + if (r != 0) + return r; + + while (c->look > 0) { + unsigned ahead; + unsigned max_ahead; + int l1, l2, l3; + + m_len = c->m_len; + m_off = c->m_off; + + assert(c->bp == c->ip - c->look); + assert(c->bp >= in); + if (lit == 0) + ii = c->bp; + assert(ii + lit == c->bp); + assert(swd->b_char == *(c->bp)); + + if (m_len < 2 + || (m_len == 2 && (m_off > M1_MAX_OFFSET || lit == 0 || lit >= 4)) + /* Do not accept this match for compressed-data compatibility + * with LZO v1.01 and before + * [ might be a problem for decompress() and optimize() ] + */ + || (m_len == 2 && op == out) + || (op == out && lit == 0) + ) { + /* a literal */ + m_len = 0; + } + else if (m_len == M2_MIN_LEN) { + /* compression ratio improves if we code a literal in some cases */ + if (m_off > MX_MAX_OFFSET && lit >= 4) + m_len = 0; + } + + if (m_len == 0) { + /* a literal */ + lit++; + swd->max_chain = max_chain; + r = find_match(c, swd, 1, 0); + assert(r == 0); + continue; + } + + /* a match */ +#if defined(SWD_BEST_OFF) + if (swd->use_best_off) + better_match(swd, &m_len, &m_off); +#endif + + /* shall we try a lazy match ? */ + ahead = 0; + if (m_len >= max_lazy) { + /* no */ + l1 = 0; + max_ahead = 0; + } else { + /* yes, try a lazy match */ + l1 = len_of_coded_match(m_len, m_off, lit); + assert(l1 > 0); + max_ahead = LZO_MIN(2, (unsigned)l1 - 1); + } + + + while (ahead < max_ahead && c->look > m_len) { + int lazy_match_min_gain; + + if (m_len >= good_length) + swd->max_chain = max_chain >> 2; + else + swd->max_chain = max_chain; + r = find_match(c, swd, 1, 0); + ahead++; + + assert(r == 0); + assert(c->look > 0); + assert(ii + lit + ahead == c->bp); + + if (c->m_len < m_len) + continue; + if (c->m_len == m_len && c->m_off >= m_off) + continue; +#if defined(SWD_BEST_OFF) + if (swd->use_best_off) + better_match(swd, &c->m_len, &c->m_off); +#endif + l2 = len_of_coded_match(c->m_len, c->m_off, lit+ahead); + if (l2 < 0) + continue; + + /* compressed-data compatibility [see above] */ + l3 = (op == out) ? -1 : len_of_coded_match(ahead, m_off, lit); + + lazy_match_min_gain = min_gain(ahead, lit, lit+ahead, l1, l2, l3); + if (c->m_len >= m_len + lazy_match_min_gain) { + if (l3 > 0) { + /* code previous run */ + op = code_run(c, op, ii, lit); + lit = 0; + /* code shortened match */ + op = code_match(c, op, ahead, m_off); + } else { + lit += ahead; + assert(ii + lit == c->bp); + } + goto lazy_match_done; + } + } + + assert(ii + lit + ahead == c->bp); + + /* 1 - code run */ + op = code_run(c, op, ii, lit); + lit = 0; + + /* 2 - code match */ + op = code_match(c, op, m_len, m_off); + swd->max_chain = max_chain; + r = find_match(c, swd, m_len, 1+ahead); + assert(r == 0); + + lazy_match_done: ; + } + + /* store final run */ + if (lit > 0) + op = STORE_RUN(c, op, ii, lit); + +#if defined(LZO_EOF_CODE) + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; +#endif + + *out_len = op - out; + + return LZO_E_OK; +} + + +/*********************************************************************** +// +************************************************************************/ +int lzo1x_999_compress_level(const uint8_t *in, unsigned in_len, + uint8_t *out, unsigned *out_len, + void *wrkmem, + int compression_level) +{ + static const struct { + uint16_t good_length; + uint16_t max_lazy; + uint16_t max_chain; + uint16_t use_best_off; + } c[3] = { + { 8, 32, 256, 0 }, + { 32, 128, 2048, 1 }, + { SWD_F, SWD_F, 4096, 1 } /* max. compression */ + }; + + if (compression_level < 7 || compression_level > 9) + return LZO_E_ERROR; + + compression_level -= 7; + return lzo1x_999_compress_internal(in, in_len, out, out_len, wrkmem, + c[compression_level].good_length, + c[compression_level].max_lazy, + c[compression_level].max_chain, + c[compression_level].use_best_off); +} diff --git a/busybox/archival/libarchive/lzo1x_c.c b/busybox/archival/libarchive/lzo1x_c.c new file mode 100644 index 00000000000000..8c77072ab8151b --- /dev/null +++ b/busybox/archival/libarchive/lzo1x_c.c @@ -0,0 +1,296 @@ +/* implementation of the LZO1[XY]-1 compression algorithm + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzo/ + + The LZO library is free software; you can 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/*********************************************************************** +// compress a block of data. +************************************************************************/ +static NOINLINE unsigned +do_compress(const uint8_t* in, unsigned in_len, + uint8_t* out, unsigned* out_len, + void* wrkmem) +{ + register const uint8_t* ip; + uint8_t* op; + const uint8_t* const in_end = in + in_len; + const uint8_t* const ip_end = in + in_len - M2_MAX_LEN - 5; + const uint8_t* ii; + const void* *const dict = (const void**) wrkmem; + + op = out; + ip = in; + ii = ip; + + ip += 4; + for (;;) { + register const uint8_t* m_pos; + unsigned m_off; + unsigned m_len; + unsigned dindex; + + D_INDEX1(dindex,ip); + GINDEX(m_pos,m_off,dict,dindex,in); + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) + goto literal; +#if 1 + if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) + goto try_match; + D_INDEX2(dindex,ip); +#endif + GINDEX(m_pos,m_off,dict,dindex,in); + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) + goto literal; + if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) + goto try_match; + goto literal; + + try_match: +#if 1 && defined(LZO_UNALIGNED_OK_2) + if (* (const lzo_ushortp) m_pos != * (const lzo_ushortp) ip) +#else + if (m_pos[0] != ip[0] || m_pos[1] != ip[1]) +#endif + { + } else { + if (m_pos[2] == ip[2]) { +#if 0 + if (m_off <= M2_MAX_OFFSET) + goto match; + if (lit <= 3) + goto match; + if (lit == 3) { /* better compression, but slower */ + assert(op - 2 > out); op[-2] |= (uint8_t)(3); + *op++ = *ii++; *op++ = *ii++; *op++ = *ii++; + goto code_match; + } + if (m_pos[3] == ip[3]) +#endif + goto match; + } + else { + /* still need a better way for finding M1 matches */ +#if 0 + /* a M1 match */ +#if 0 + if (m_off <= M1_MAX_OFFSET && lit > 0 && lit <= 3) +#else + if (m_off <= M1_MAX_OFFSET && lit == 3) +#endif + { + register unsigned t; + + t = lit; + assert(op - 2 > out); op[-2] |= (uint8_t)(t); + do *op++ = *ii++; while (--t > 0); + assert(ii == ip); + m_off -= 1; + *op++ = (uint8_t)(M1_MARKER | ((m_off & 3) << 2)); + *op++ = (uint8_t)(m_off >> 2); + ip += 2; + goto match_done; + } +#endif + } + } + + /* a literal */ + literal: + UPDATE_I(dict, 0, dindex, ip, in); + ++ip; + if (ip >= ip_end) + break; + continue; + + /* a match */ +match: + UPDATE_I(dict, 0, dindex, ip, in); + /* store current literal run */ + if (pd(ip, ii) > 0) { + register unsigned t = pd(ip, ii); + + if (t <= 3) { + assert(op - 2 > out); + op[-2] |= (uint8_t)(t); + } + else if (t <= 18) + *op++ = (uint8_t)(t - 3); + else { + register unsigned tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = (uint8_t)(tt); + } + do *op++ = *ii++; while (--t > 0); + } + + /* code the match */ + assert(ii == ip); + ip += 3; + if (m_pos[3] != *ip++ || m_pos[4] != *ip++ || m_pos[5] != *ip++ + || m_pos[6] != *ip++ || m_pos[7] != *ip++ || m_pos[8] != *ip++ +#ifdef LZO1Y + || m_pos[ 9] != *ip++ || m_pos[10] != *ip++ || m_pos[11] != *ip++ + || m_pos[12] != *ip++ || m_pos[13] != *ip++ || m_pos[14] != *ip++ +#endif + ) { + --ip; + m_len = pd(ip, ii); + assert(m_len >= 3); + assert(m_len <= M2_MAX_LEN); + + if (m_off <= M2_MAX_OFFSET) { + m_off -= 1; +#if defined(LZO1X) + *op++ = (uint8_t)(((m_len - 1) << 5) | ((m_off & 7) << 2)); + *op++ = (uint8_t)(m_off >> 3); +#elif defined(LZO1Y) + *op++ = (uint8_t)(((m_len + 1) << 4) | ((m_off & 3) << 2)); + *op++ = (uint8_t)(m_off >> 2); +#endif + } + else if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + *op++ = (uint8_t)(M3_MARKER | (m_len - 2)); + goto m3_m4_offset; + } else { +#if defined(LZO1X) + m_off -= 0x4000; + assert(m_off > 0); + assert(m_off <= 0x7fff); + *op++ = (uint8_t)(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2)); + goto m3_m4_offset; +#elif defined(LZO1Y) + goto m4_match; +#endif + } + } + else { + { + const uint8_t* end = in_end; + const uint8_t* m = m_pos + M2_MAX_LEN + 1; + while (ip < end && *m == *ip) + m++, ip++; + m_len = pd(ip, ii); + } + assert(m_len > M2_MAX_LEN); + + if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + if (m_len <= 33) + *op++ = (uint8_t)(M3_MARKER | (m_len - 2)); + else { + m_len -= 33; + *op++ = M3_MARKER | 0; + goto m3_m4_len; + } + } else { +#if defined(LZO1Y) + m4_match: +#endif + m_off -= 0x4000; + assert(m_off > 0); + assert(m_off <= 0x7fff); + if (m_len <= M4_MAX_LEN) + *op++ = (uint8_t)(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2)); + else { + m_len -= M4_MAX_LEN; + *op++ = (uint8_t)(M4_MARKER | ((m_off & 0x4000) >> 11)); + m3_m4_len: + while (m_len > 255) { + m_len -= 255; + *op++ = 0; + } + assert(m_len > 0); + *op++ = (uint8_t)(m_len); + } + } + m3_m4_offset: + *op++ = (uint8_t)((m_off & 63) << 2); + *op++ = (uint8_t)(m_off >> 6); + } +#if 0 + match_done: +#endif + ii = ip; + if (ip >= ip_end) + break; + } + + *out_len = pd(op, out); + return pd(in_end, ii); +} + +/*********************************************************************** +// public entry point +************************************************************************/ +int DO_COMPRESS(const uint8_t* in, unsigned in_len, + uint8_t* out, unsigned* out_len, + void* wrkmem) +{ + uint8_t* op = out; + unsigned t; + + if (in_len <= M2_MAX_LEN + 5) + t = in_len; + else { + t = do_compress(in,in_len,op,out_len,wrkmem); + op += *out_len; + } + + if (t > 0) { + const uint8_t* ii = in + in_len - t; + + if (op == out && t <= 238) + *op++ = (uint8_t)(17 + t); + else if (t <= 3) + op[-2] |= (uint8_t)(t); + else if (t <= 18) + *op++ = (uint8_t)(t - 3); + else { + unsigned tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = (uint8_t)(tt); + } + do *op++ = *ii++; while (--t > 0); + } + + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; + + *out_len = pd(op, out); + return 0; /*LZO_E_OK*/ +} diff --git a/busybox/archival/libarchive/lzo1x_d.c b/busybox/archival/libarchive/lzo1x_d.c new file mode 100644 index 00000000000000..43cf4a04e93fba --- /dev/null +++ b/busybox/archival/libarchive/lzo1x_d.c @@ -0,0 +1,422 @@ +/* implementation of the LZO1X decompression algorithm + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzo/ + + The LZO library is free software; you can 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. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "libbb.h" +#include "liblzo.h" + +/*********************************************************************** +// decompress a block of data. +************************************************************************/ +/* safe decompression with overrun testing */ +int lzo1x_decompress_safe(const uint8_t* in, unsigned in_len, + uint8_t* out, unsigned* out_len /*, void* wrkmem */) +{ + register uint8_t* op; + register const uint8_t* ip; + register unsigned t; +#if defined(COPY_DICT) + unsigned m_off; + const uint8_t* dict_end; +#else + register const uint8_t* m_pos = NULL; /* possibly not needed */ +#endif + const uint8_t* const ip_end = in + in_len; +#if defined(HAVE_ANY_OP) + uint8_t* const op_end = out + *out_len; +#endif +#if defined(LZO1Z) + unsigned last_m_off = 0; +#endif + +// LZO_UNUSED(wrkmem); + +#if defined(COPY_DICT) + if (dict) { + if (dict_len > M4_MAX_OFFSET) { + dict += dict_len - M4_MAX_OFFSET; + dict_len = M4_MAX_OFFSET; + } + dict_end = dict + dict_len; + } else { + dict_len = 0; + dict_end = NULL; + } +#endif /* COPY_DICT */ + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) { + t = *ip++ - 17; + if (t < 4) + goto match_next; + assert(t > 0); NEED_OP(t); NEED_IP(t+1); + do *op++ = *ip++; while (--t > 0); + goto first_literal_run; + } + + while (TEST_IP && TEST_OP) { + t = *ip++; + if (t >= 16) + goto match; + /* a literal run */ + if (t == 0) { + NEED_IP(1); + while (*ip == 0) { + t += 255; + ip++; + NEED_IP(1); + } + TEST_IV(t); + t += 15 + *ip++; + } + /* copy literals */ + assert(t > 0); + NEED_OP(t+3); + NEED_IP(t+4); +#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4) +# if !defined(LZO_UNALIGNED_OK_4) + if (PTR_ALIGNED2_4(op, ip)) +# endif + { + COPY4(op, ip); + op += 4; + ip += 4; + if (--t > 0) { + if (t >= 4) { + do { + COPY4(op, ip); + op += 4; + ip += 4; + t -= 4; + } while (t >= 4); + if (t > 0) + do *op++ = *ip++; while (--t > 0); + } else { + do *op++ = *ip++; while (--t > 0); + } + } + } +# if !defined(LZO_UNALIGNED_OK_4) + else +# endif +#endif +#if !defined(LZO_UNALIGNED_OK_4) + { + *op++ = *ip++; + *op++ = *ip++; + *op++ = *ip++; + do *op++ = *ip++; while (--t > 0); + } +#endif + + first_literal_run: + t = *ip++; + if (t >= 16) + goto match; +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(3); + t = 3; COPY_DICT(t,m_off) +#else /* !COPY_DICT */ +#if defined(LZO1Z) + t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LB(m_pos); NEED_OP(3); + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos; +#endif /* COPY_DICT */ + goto match_done; + + /* handle matches */ + do { + match: + if (t >= 64) { /* a M2 match */ +#if defined(COPY_DICT) +#if defined(LZO1X) + m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3); + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2); + t = (t >> 4) - 3; +#elif defined(LZO1Z) + m_off = t & 0x1f; + if (m_off >= 0x1c) + m_off = last_m_off; + else { + m_off = 1 + (m_off << 6) + (*ip++ >> 2); + last_m_off = m_off; + } + t = (t >> 5) - 1; +#endif +#else /* !COPY_DICT */ +#if defined(LZO1X) + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos = op - 1; + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#elif defined(LZO1Z) + { + unsigned off = t & 0x1f; + m_pos = op; + if (off >= 0x1c) { + assert(last_m_off > 0); + m_pos -= last_m_off; + } else { + off = 1 + (off << 6) + (*ip++ >> 2); + m_pos -= off; + last_m_off = off; + } + } + t = (t >> 5) - 1; +#endif + TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); + goto copy_match; +#endif /* COPY_DICT */ + } + else if (t >= 32) { /* a M3 match */ + t &= 31; + if (t == 0) { + NEED_IP(1); + while (*ip == 0) { + t += 255; + ip++; + NEED_IP(1); + } + TEST_IV(t); + t += 31 + *ip++; + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (ip[0] << 6) + (ip[1] >> 2); + last_m_off = m_off; +#else + m_off = 1 + (ip[0] >> 2) + (ip[1] << 6); +#endif +#else /* !COPY_DICT */ +#if defined(LZO1Z) + { + unsigned off = 1 + (ip[0] << 6) + (ip[1] >> 2); + m_pos = op - off; + last_m_off = off; + } +#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN) + m_pos = op - 1; + m_pos -= (* (const lzo_ushortp) ip) >> 2; +#else + m_pos = op - 1; + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif +#endif /* COPY_DICT */ + ip += 2; + } + else if (t >= 16) { /* a M4 match */ +#if defined(COPY_DICT) + m_off = (t & 8) << 11; +#else /* !COPY_DICT */ + m_pos = op; + m_pos -= (t & 8) << 11; +#endif /* COPY_DICT */ + t &= 7; + if (t == 0) { + NEED_IP(1); + while (*ip == 0) { + t += 255; + ip++; + NEED_IP(1); + } + TEST_IV(t); + t += 7 + *ip++; + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off += (ip[0] << 6) + (ip[1] >> 2); +#else + m_off += (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_off == 0) + goto eof_found; + m_off += 0x4000; +#if defined(LZO1Z) + last_m_off = m_off; +#endif +#else /* !COPY_DICT */ +#if defined(LZO1Z) + m_pos -= (ip[0] << 6) + (ip[1] >> 2); +#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN) + m_pos -= (* (const lzo_ushortp) ip) >> 2; +#else + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; +#if defined(LZO1Z) + last_m_off = pd((const uint8_t*)op, m_pos); +#endif +#endif /* COPY_DICT */ + } + else { /* a M1 match */ +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = 1 + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(2); + t = 2; COPY_DICT(t,m_off) +#else /* !COPY_DICT */ +#if defined(LZO1Z) + t = 1 + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LB(m_pos); NEED_OP(2); + *op++ = *m_pos++; + *op++ = *m_pos; +#endif /* COPY_DICT */ + goto match_done; + } + + /* copy match */ +#if defined(COPY_DICT) + + NEED_OP(t+3-1); + t += 3-1; COPY_DICT(t,m_off) + +#else /* !COPY_DICT */ + + TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1); +#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4) +# if !defined(LZO_UNALIGNED_OK_4) + if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos)) { + assert((op - m_pos) >= 4); /* both pointers are aligned */ +# else + if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) { +# endif + COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4 - (3 - 1); + do { + COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4; + } while (t >= 4); + if (t > 0) + do *op++ = *m_pos++; while (--t > 0); + } + else +#endif + { + copy_match: + *op++ = *m_pos++; *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + +#endif /* COPY_DICT */ + + match_done: +#if defined(LZO1Z) + t = ip[-1] & 3; +#else + t = ip[-2] & 3; +#endif + if (t == 0) + break; + + /* copy literals */ + match_next: + assert(t > 0); + assert(t < 4); + NEED_OP(t); + NEED_IP(t+1); +#if 0 + do *op++ = *ip++; while (--t > 0); +#else + *op++ = *ip++; + if (t > 1) { + *op++ = *ip++; + if (t > 2) + *op++ = *ip++; + } +#endif + t = *ip++; + } while (TEST_IP && TEST_OP); + } + +//#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP) + /* no EOF code was found */ + *out_len = pd(op, out); + return LZO_E_EOF_NOT_FOUND; +//#endif + + eof_found: + assert(t == 1); + *out_len = pd(op, out); + return (ip == ip_end ? LZO_E_OK : + (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); + +//#if defined(HAVE_NEED_IP) + input_overrun: + *out_len = pd(op, out); + return LZO_E_INPUT_OVERRUN; +//#endif + +//#if defined(HAVE_NEED_OP) + output_overrun: + *out_len = pd(op, out); + return LZO_E_OUTPUT_OVERRUN; +//#endif + +//#if defined(LZO_TEST_OVERRUN_LOOKBEHIND) + lookbehind_overrun: + *out_len = pd(op, out); + return LZO_E_LOOKBEHIND_OVERRUN; +//#endif +} diff --git a/busybox/archival/libarchive/open_transformer.c b/busybox/archival/libarchive/open_transformer.c new file mode 100644 index 00000000000000..9fefd4aaddcc99 --- /dev/null +++ b/busybox/archival/libarchive/open_transformer.c @@ -0,0 +1,377 @@ +/* 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" + +void FAST_FUNC init_transformer_state(transformer_state_t *xstate) +{ + memset(xstate, 0, sizeof(*xstate)); +} + +int FAST_FUNC check_signature16(transformer_state_t *xstate, unsigned magic16) +{ + if (!xstate->signature_skipped) { + uint16_t magic2; + if (full_read(xstate->src_fd, &magic2, 2) != 2 || magic2 != magic16) { + bb_error_msg("invalid magic"); + 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; + + if (!signo) { + /* block waiting for any child */ + if (wait(&status) < 0) +//FIXME: check EINTR? + return; /* probably there are no children */ + goto check_status; + } + + /* Wait for any child without blocking */ + for (;;) { + if (wait_any_nohang(&status) < 0) +//FIXME: check EINTR? + /* wait failed?! I'm confused... */ + return; + check_status: + /*if (WIFEXITED(status) && WEXITSTATUS(status) == 0)*/ + /* On Linux, the above can be checked simply as: */ + if (status == 0) + /* this child exited with 0 */ + continue; + /* Cannot happen: + if (!WIFSIGNALED(status) && !WIFEXITED(status)) ???; + */ + bb_got_signal = 1; + } +} + +/* transformer(), more than meets the eye */ +#if BB_MMU +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 fork_transformer(int fd, const char *transform_prog) +#endif +{ + struct fd_pair fd_pipe; + int pid; + + xpiped_pair(fd_pipe); + pid = BB_MMU ? xfork() : xvfork(); + if (pid == 0) { + /* Child */ + close(fd_pipe.rd); /* we don't want to read from the parent */ + // FIXME: error check? +#if BB_MMU + { + IF_DESKTOP(long long) int r; + 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); + } + /* must be _exit! bug was actually seen here */ + _exit(/*error if:*/ r < 0); + } +#else + { + char *argv[4]; + xmove_fd(fd, 0); + xmove_fd(fd_pipe.wr, 1); + argv[0] = (char*)transform_prog; + argv[1] = (char*)"-cf"; + argv[2] = (char*)"-"; + argv[3] = NULL; + BB_EXECVP(transform_prog, argv); + bb_perror_msg_and_die("can't execute '%s'", transform_prog); + } +#endif + /* notreached */ + } + + /* parent process */ + close(fd_pipe.wr); /* don't want to write to the child */ + xmove_fd(fd_pipe.rd, fd); +} + + +#if SEAMLESS_COMPRESSION + +/* Used by e.g. rpm which gives us a fd without filename, + * thus we can't guess the format from filename's extension. + */ +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; + 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. */ + xread(fd, magic.b16, sizeof(magic.b16[0])); + if (ENABLE_FEATURE_SEAMLESS_GZ + && magic.b16[0] == GZIP_MAGIC + ) { + 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 + ) { + 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 + ) { + xstate->signature_skipped = 6; + xread(fd, magic.b32, sizeof(magic.b32[0])); + if (magic.b32[0] == XZ_MAGIC2) { + xstate->xformer = unpack_xz_stream; + USE_FOR_NOMMU(xstate->xformer_prog = "unxz";) + goto found_magic; + } + } + + /* No known magic seen */ + if (fail_if_not_compressed) + bb_error_msg_and_die("no gzip" + IF_FEATURE_SEAMLESS_BZ2("/bzip2") + IF_FEATURE_SEAMLESS_XZ("/xz") + " magic"); + + /* 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; +} + +static void fork_transformer_and_free(transformer_state_t *xstate) +{ +# if BB_MMU + fork_transformer_with_no_sig(xstate->src_fd, xstate->xformer); +# else + /* NOMMU version of fork_transformer execs + * an external unzipper that wants + * file position at the start of the file. + */ + xlseek(xstate->src_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); +} + +/* 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->xformer) { + free(xstate); + return 1; + } + + fork_transformer_and_free(xstate); + return 0; +} +#if ENABLE_FEATURE_SEAMLESS_LZMA +/* ...and custom version for LZMA */ +void FAST_FUNC setup_lzma_on_fd(int fd) +{ + transformer_state_t *xstate = xzalloc(sizeof(*xstate)); + xstate->src_fd = fd; + xstate->xformer = unpack_lzma_stream; + USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";) + fork_transformer_and_free(xstate); +} +#endif + +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 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) { + xstate = xzalloc(sizeof(*xstate)); + xstate->src_fd = fd; + xstate->xformer = unpack_lzma_stream; + USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";) + return xstate; + } + } + + 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; +} + +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; + + fd = open_zipped(fname, /*fail_if_not_compressed:*/ 0); + if (fd < 0) + return NULL; + + image = xmalloc_read(fd, maxsz_p); + if (!image) + bb_perror_msg("read error from '%s'", fname); + close(fd); + return image; +# endif +} + +#endif /* SEAMLESS_COMPRESSION */ diff --git a/busybox/archival/libarchive/seek_by_jump.c b/busybox/archival/libarchive/seek_by_jump.c new file mode 100644 index 00000000000000..232d97e53698bd --- /dev/null +++ b/busybox/archival/libarchive/seek_by_jump.c @@ -0,0 +1,18 @@ +/* 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" + +void FAST_FUNC seek_by_jump(int fd, off_t amount) +{ + if (amount + && lseek(fd, amount, SEEK_CUR) == (off_t) -1 + ) { + if (errno == ESPIPE) + seek_by_read(fd, amount); + else + bb_perror_msg_and_die("seek failure"); + } +} diff --git a/busybox/archival/libarchive/seek_by_read.c b/busybox/archival/libarchive/seek_by_read.c new file mode 100644 index 00000000000000..df234635480497 --- /dev/null +++ b/busybox/archival/libarchive/seek_by_read.c @@ -0,0 +1,15 @@ +/* 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" + +/* If we are reading through a pipe, or from stdin then we can't lseek, + * we must read and discard the data to skip over it. + */ +void FAST_FUNC seek_by_read(int fd, off_t amount) +{ + if (amount) + bb_copyfd_exact_size(fd, -1, amount); +} diff --git a/busybox/archival/libarchive/unpack_ar_archive.c b/busybox/archival/libarchive/unpack_ar_archive.c new file mode 100644 index 00000000000000..4f9f89874cc31b --- /dev/null +++ b/busybox/archival/libarchive/unpack_ar_archive.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" +#include "ar.h" + +void FAST_FUNC unpack_ar_archive(archive_handle_t *ar_archive) +{ + char magic[7]; + + xread(ar_archive->src_fd, magic, AR_MAGIC_LEN); + if (!is_prefixed_with(magic, AR_MAGIC)) { + bb_error_msg_and_die("invalid ar magic"); + } + ar_archive->offset += AR_MAGIC_LEN; + + while (get_header_ar(ar_archive) == EXIT_SUCCESS) + continue; +} diff --git a/busybox/archival/libarchive/unsafe_prefix.c b/busybox/archival/libarchive/unsafe_prefix.c new file mode 100644 index 00000000000000..33e487bf9455df --- /dev/null +++ b/busybox/archival/libarchive/unsafe_prefix.c @@ -0,0 +1,35 @@ +/* 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/busybox/archival/libarchive/unsafe_symlink_target.c b/busybox/archival/libarchive/unsafe_symlink_target.c new file mode 100644 index 00000000000000..f8dc8033d24fad --- /dev/null +++ b/busybox/archival/libarchive/unsafe_symlink_target.c @@ -0,0 +1,42 @@ +/* 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" + +void FAST_FUNC create_or_remember_link(llist_t **link_placeholders, + const char *target, + const char *linkname, + int hard_link) +{ + if (hard_link || target[0] == '/' || strstr(target, "..")) { + llist_add_to_end(link_placeholders, + xasprintf("%c%s%c%s", hard_link, linkname, '\0', target) + ); + return; + } + if (symlink(target, linkname) != 0) { + /* shared message */ + bb_perror_msg_and_die("can't create %slink '%s' to '%s'", + "sym", linkname, target + ); + } +} + +void FAST_FUNC create_links_from_list(llist_t *list) +{ + while (list) { + char *target; + + target = list->data + 1 + strlen(list->data + 1) + 1; + if ((*list->data ? link : symlink) (target, list->data + 1)) { + /* shared message */ + bb_error_msg_and_die("can't create %slink '%s' to '%s'", + *list->data ? "hard" : "sym", + list->data + 1, target + ); + } + list = list->link; + } +} diff --git a/busybox/archival/libarchive/unxz/README b/busybox/archival/libarchive/unxz/README new file mode 100644 index 00000000000000..a8491203574154 --- /dev/null +++ b/busybox/archival/libarchive/unxz/README @@ -0,0 +1,135 @@ + +XZ Embedded +=========== + + XZ Embedded is a relatively small, limited implementation of the .xz + file format. Currently only decoding is implemented. + + XZ Embedded was written for use in the Linux kernel, but the code can + be easily used in other environments too, including regular userspace + applications. See userspace/xzminidec.c for an example program. + + This README contains information that is useful only when the copy + of XZ Embedded isn't part of the Linux kernel tree. You should also + read linux/Documentation/xz.txt even if you aren't using XZ Embedded + as part of Linux; information in that file is not repeated in this + README. + +Compiling the Linux kernel module + + The xz_dec module depends on crc32 module, so make sure that you have + it enabled (CONFIG_CRC32). + + Building the xz_dec and xz_dec_test modules without support for BCJ + filters: + + cd linux/lib/xz + make -C /path/to/kernel/source \ + KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \ + CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m + + Building the xz_dec and xz_dec_test modules with support for BCJ + filters: + + cd linux/lib/xz + make -C /path/to/kernel/source \ + KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \ + CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m CONFIG_XZ_DEC_BCJ=y \ + CONFIG_XZ_DEC_X86=y CONFIG_XZ_DEC_POWERPC=y \ + CONFIG_XZ_DEC_IA64=y CONFIG_XZ_DEC_ARM=y \ + CONFIG_XZ_DEC_ARMTHUMB=y CONFIG_XZ_DEC_SPARC=y + + If you want only one or a few of the BCJ filters, omit the appropriate + variables. CONFIG_XZ_DEC_BCJ=y is always required to build the support + code shared between all BCJ filters. + + Most people don't need the xz_dec_test module. You can skip building + it by omitting CONFIG_XZ_DEC_TEST=m from the make command line. + +Compiler requirements + + XZ Embedded should compile as either GNU-C89 (used in the Linux + kernel) or with any C99 compiler. Getting the code to compile with + non-GNU C89 compiler or a C++ compiler should be quite easy as + long as there is a data type for unsigned 64-bit integer (or the + code is modified not to support large files, which needs some more + care than just using 32-bit integer instead of 64-bit). + + If you use GCC, try to use a recent version. For example, on x86-32, + xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when + compiled with GCC 4.3.3. + +Embedding into userspace applications + + To embed the XZ decoder, copy the following files into a single + directory in your source code tree: + + linux/include/linux/xz.h + linux/lib/xz/xz_crc32.c + linux/lib/xz/xz_dec_lzma2.c + linux/lib/xz/xz_dec_stream.c + linux/lib/xz/xz_lzma2.h + linux/lib/xz/xz_private.h + linux/lib/xz/xz_stream.h + userspace/xz_config.h + + Alternatively, xz.h may be placed into a different directory but then + that directory must be in the compiler include path when compiling + the .c files. + + Your code should use only the functions declared in xz.h. The rest of + the .h files are meant only for internal use in XZ Embedded. + + You may want to modify xz_config.h to be more suitable for your build + environment. Probably you should at least skim through it even if the + default file works as is. + +BCJ filter support + + If you want support for one or more BCJ filters, you need to copy also + linux/lib/xz/xz_dec_bcj.c into your application, and use appropriate + #defines in xz_config.h or in compiler flags. You don't need these + #defines in the code that just uses XZ Embedded via xz.h, but having + them always #defined doesn't hurt either. + + #define Instruction set BCJ filter endianness + XZ_DEC_X86 x86-32 or x86-64 Little endian only + XZ_DEC_POWERPC PowerPC Big endian only + XZ_DEC_IA64 Itanium (IA-64) Big or little endian + XZ_DEC_ARM ARM Little endian only + XZ_DEC_ARMTHUMB ARM-Thumb Little endian only + XZ_DEC_SPARC SPARC Big or little endian + + While some architectures are (partially) bi-endian, the endianness + setting doesn't change the endianness of the instructions on all + architectures. That's why Itanium and SPARC filters work for both big + and little endian executables (Itanium has little endian instructions + and SPARC has big endian instructions). + + There currently is no filter for little endian PowerPC or big endian + ARM or ARM-Thumb. Implementing filters for them can be considered if + there is a need for such filters in real-world applications. + +Notes about shared libraries + + If you are including XZ Embedded into a shared library, you very + probably should rename the xz_* functions to prevent symbol + conflicts in case your library is linked against some other library + or application that also has XZ Embedded in it (which may even be + a different version of XZ Embedded). TODO: Provide an easy way + to do this. + + Please don't create a shared library of XZ Embedded itself unless + it is fine to rebuild everything depending on that shared library + everytime you upgrade to a newer version of XZ Embedded. There are + no API or ABI stability guarantees between different versions of + XZ Embedded. + +Specifying the calling convention + + XZ_FUNC macro was included to support declaring functions with __init + in Linux. Outside Linux, it can be used to specify the calling + convention on systems that support multiple calling conventions. + For example, on Windows, you may make all functions use the stdcall + calling convention by defining XZ_FUNC=__stdcall when building and + using the functions from XZ Embedded. diff --git a/busybox/archival/libarchive/unxz/xz.h b/busybox/archival/libarchive/unxz/xz.h new file mode 100644 index 00000000000000..e0b22db56d121d --- /dev/null +++ b/busybox/archival/libarchive/unxz/xz.h @@ -0,0 +1,280 @@ +/* + * XZ decompressor + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_H +#define XZ_H + +#ifdef __KERNEL__ +# include +# include +#else +# include +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* In Linux, this is used to make extern functions static when needed. */ +#ifndef XZ_EXTERN +# define XZ_EXTERN extern +#endif + +/* In Linux, this is used to mark the functions with __init when needed. */ +#ifndef XZ_FUNC +# define XZ_FUNC +#endif + +/** + * enum xz_mode - Operation mode + * + * @XZ_SINGLE: Single-call mode. This uses less RAM than + * than multi-call modes, because the LZMA2 + * dictionary doesn't need to be allocated as + * part of the decoder state. All required data + * structures are allocated at initialization, + * so xz_dec_run() cannot return XZ_MEM_ERROR. + * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2 + * dictionary buffer. All data structures are + * allocated at initialization, so xz_dec_run() + * cannot return XZ_MEM_ERROR. + * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is + * allocated once the required size has been + * parsed from the stream headers. If the + * allocation fails, xz_dec_run() will return + * XZ_MEM_ERROR. + * + * It is possible to enable support only for a subset of the above + * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC, + * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled + * with support for all operation modes, but the preboot code may + * be built with fewer features to minimize code size. + */ +enum xz_mode { + XZ_SINGLE, + XZ_PREALLOC, + XZ_DYNALLOC +}; + +/** + * enum xz_ret - Return codes + * @XZ_OK: Everything is OK so far. More input or more + * output space is required to continue. This + * return code is possible only in multi-call mode + * (XZ_PREALLOC or XZ_DYNALLOC). + * @XZ_STREAM_END: Operation finished successfully. + * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding + * is still possible in multi-call mode by simply + * calling xz_dec_run() again. + * Note that this return value is used only if + * XZ_DEC_ANY_CHECK was defined at build time, + * which is not used in the kernel. Unsupported + * check types return XZ_OPTIONS_ERROR if + * XZ_DEC_ANY_CHECK was not defined at build time. + * @XZ_MEM_ERROR: Allocating memory failed. This return code is + * possible only if the decoder was initialized + * with XZ_DYNALLOC. The amount of memory that was + * tried to be allocated was no more than the + * dict_max argument given to xz_dec_init(). + * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than + * allowed by the dict_max argument given to + * xz_dec_init(). This return value is possible + * only in multi-call mode (XZ_PREALLOC or + * XZ_DYNALLOC); the single-call mode (XZ_SINGLE) + * ignores the dict_max argument. + * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic + * bytes). + * @XZ_OPTIONS_ERROR: This implementation doesn't support the requested + * compression options. In the decoder this means + * that the header CRC32 matches, but the header + * itself specifies something that we don't support. + * @XZ_DATA_ERROR: Compressed data is corrupt. + * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly + * different between multi-call and single-call + * mode; more information below. + * + * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls + * to XZ code cannot consume any input and cannot produce any new output. + * This happens when there is no new input available, or the output buffer + * is full while at least one output byte is still pending. Assuming your + * code is not buggy, you can get this error only when decoding a compressed + * stream that is truncated or otherwise corrupt. + * + * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer + * is too small or the compressed input is corrupt in a way that makes the + * decoder produce more output than the caller expected. When it is + * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR + * is used instead of XZ_BUF_ERROR. + */ +enum xz_ret { + XZ_OK, + XZ_STREAM_END, + XZ_UNSUPPORTED_CHECK, + XZ_MEM_ERROR, + XZ_MEMLIMIT_ERROR, + XZ_FORMAT_ERROR, + XZ_OPTIONS_ERROR, + XZ_DATA_ERROR, + XZ_BUF_ERROR +}; + +/** + * struct xz_buf - Passing input and output buffers to XZ code + * @in: Beginning of the input buffer. This may be NULL if and only + * if in_pos is equal to in_size. + * @in_pos: Current position in the input buffer. This must not exceed + * in_size. + * @in_size: Size of the input buffer + * @out: Beginning of the output buffer. This may be NULL if and only + * if out_pos is equal to out_size. + * @out_pos: Current position in the output buffer. This must not exceed + * out_size. + * @out_size: Size of the output buffer + * + * Only the contents of the output buffer from out[out_pos] onward, and + * the variables in_pos and out_pos are modified by the XZ code. + */ +struct xz_buf { + const uint8_t *in; + size_t in_pos; + size_t in_size; + + uint8_t *out; + size_t out_pos; + size_t out_size; +}; + +/** + * struct xz_dec - Opaque type to hold the XZ decoder state + */ +struct xz_dec; + +/** + * xz_dec_init() - Allocate and initialize a XZ decoder state + * @mode: Operation mode + * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for + * multi-call decoding. This is ignored in single-call mode + * (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes + * or 2^n + 2^(n-1) bytes (the latter sizes are less common + * in practice), so other values for dict_max don't make sense. + * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB, + * 512 KiB, and 1 MiB are probably the only reasonable values, + * except for kernel and initramfs images where a bigger + * dictionary can be fine and useful. + * + * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at + * once. The caller must provide enough output space or the decoding will + * fail. The output space is used as the dictionary buffer, which is why + * there is no need to allocate the dictionary as part of the decoder's + * internal state. + * + * Because the output buffer is used as the workspace, streams encoded using + * a big dictionary are not a problem in single-call mode. It is enough that + * the output buffer is big enough to hold the actual uncompressed data; it + * can be smaller than the dictionary size stored in the stream headers. + * + * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes + * of memory is preallocated for the LZMA2 dictionary. This way there is no + * risk that xz_dec_run() could run out of memory, since xz_dec_run() will + * never allocate any memory. Instead, if the preallocated dictionary is too + * small for decoding the given input stream, xz_dec_run() will return + * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be + * decoded to avoid allocating excessive amount of memory for the dictionary. + * + * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC): + * dict_max specifies the maximum allowed dictionary size that xz_dec_run() + * may allocate once it has parsed the dictionary size from the stream + * headers. This way excessive allocations can be avoided while still + * limiting the maximum memory usage to a sane value to prevent running the + * system out of memory when decompressing streams from untrusted sources. + * + * On success, xz_dec_init() returns a pointer to struct xz_dec, which is + * ready to be used with xz_dec_run(). If memory allocation fails, + * xz_dec_init() returns NULL. + */ +XZ_EXTERN struct xz_dec * XZ_FUNC xz_dec_init( + enum xz_mode mode, uint32_t dict_max); + +/** + * xz_dec_run() - Run the XZ decoder + * @s: Decoder state allocated using xz_dec_init() + * @b: Input and output buffers + * + * The possible return values depend on build options and operation mode. + * See enum xz_ret for details. + * + * Note that if an error occurs in single-call mode (return value is not + * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the + * contents of the output buffer from b->out[b->out_pos] onward are + * undefined. This is true even after XZ_BUF_ERROR, because with some filter + * chains, there may be a second pass over the output buffer, and this pass + * cannot be properly done if the output buffer is truncated. Thus, you + * cannot give the single-call decoder a too small buffer and then expect to + * get that amount valid data from the beginning of the stream. You must use + * the multi-call decoder if you don't want to uncompress the whole stream. + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_run(struct xz_dec *s, struct xz_buf *b); + +/** + * xz_dec_reset() - Reset an already allocated decoder state + * @s: Decoder state allocated using xz_dec_init() + * + * This function can be used to reset the multi-call decoder state without + * freeing and reallocating memory with xz_dec_end() and xz_dec_init(). + * + * In single-call mode, xz_dec_reset() is always called in the beginning of + * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in + * multi-call mode. + */ +XZ_EXTERN void XZ_FUNC xz_dec_reset(struct xz_dec *s); + +/** + * xz_dec_end() - Free the memory allocated for the decoder state + * @s: Decoder state allocated using xz_dec_init(). If s is NULL, + * this function does nothing. + */ +XZ_EXTERN void XZ_FUNC xz_dec_end(struct xz_dec *s); + +/* + * Standalone build (userspace build or in-kernel build for boot time use) + * needs a CRC32 implementation. For normal in-kernel use, kernel's own + * CRC32 module is used instead, and users of this module don't need to + * care about the functions below. + */ +#ifndef XZ_INTERNAL_CRC32 +# ifdef __KERNEL__ +# define XZ_INTERNAL_CRC32 0 +# else +# define XZ_INTERNAL_CRC32 1 +# endif +#endif + +#if XZ_INTERNAL_CRC32 +/* + * This must be called before any other xz_* function to initialize + * the CRC32 lookup table. + */ +XZ_EXTERN void XZ_FUNC xz_crc32_init(void); + +/* + * Update CRC32 value using the polynomial from IEEE-802.3. To start a new + * calculation, the third argument must be zero. To continue the calculation, + * the previously returned value is passed as the third argument. + */ +XZ_EXTERN uint32_t XZ_FUNC xz_crc32( + const uint8_t *buf, size_t size, uint32_t crc); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/busybox/archival/libarchive/unxz/xz_config.h b/busybox/archival/libarchive/unxz/xz_config.h new file mode 100644 index 00000000000000..187e1cbed6cfa6 --- /dev/null +++ b/busybox/archival/libarchive/unxz/xz_config.h @@ -0,0 +1,123 @@ +/* + * Private includes and definitions for userspace use of XZ Embedded + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_CONFIG_H +#define XZ_CONFIG_H + +/* Uncomment as needed to enable BCJ filter decoders. */ +/* #define XZ_DEC_X86 */ +/* #define XZ_DEC_POWERPC */ +/* #define XZ_DEC_IA64 */ +/* #define XZ_DEC_ARM */ +/* #define XZ_DEC_ARMTHUMB */ +/* #define XZ_DEC_SPARC */ + +#include +#include +#include + +#include "xz.h" + +#define kmalloc(size, flags) malloc(size) +#define kfree(ptr) free(ptr) +#define vmalloc(size) malloc(size) +#define vfree(ptr) free(ptr) + +#define memeq(a, b, size) (memcmp(a, b, size) == 0) +#define memzero(buf, size) memset(buf, 0, size) + +#undef min +#undef min_t +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define min_t(type, x, y) min(x, y) + +/* + * Some functions have been marked with __always_inline to keep the + * performance reasonable even when the compiler is optimizing for + * small code size. You may be able to save a few bytes by #defining + * __always_inline to plain inline, but don't complain if the code + * becomes slow. + * + * NOTE: System headers on GNU/Linux may #define this macro already, + * so if you want to change it, you need to #undef it first. + */ +#ifndef __always_inline +# ifdef __GNUC__ +# define __always_inline \ + inline __attribute__((__always_inline__)) +# else +# define __always_inline inline +# endif +#endif + +/* + * Some functions are marked to never be inlined to reduce stack usage. + * If you don't care about stack usage, you may want to modify this so + * that noinline_for_stack is #defined to be empty even when using GCC. + * Doing so may save a few bytes in binary size. + */ +#ifndef noinline_for_stack +# ifdef __GNUC__ +# define noinline_for_stack __attribute__((__noinline__)) +# else +# define noinline_for_stack +# endif +#endif + +/* Inline functions to access unaligned unsigned 32-bit integers */ +#ifndef get_unaligned_le32 +static inline uint32_t XZ_FUNC get_unaligned_le32(const uint8_t *buf) +{ + return (uint32_t)buf[0] + | ((uint32_t)buf[1] << 8) + | ((uint32_t)buf[2] << 16) + | ((uint32_t)buf[3] << 24); +} +#endif + +#ifndef get_unaligned_be32 +static inline uint32_t XZ_FUNC get_unaligned_be32(const uint8_t *buf) +{ + return (uint32_t)(buf[0] << 24) + | ((uint32_t)buf[1] << 16) + | ((uint32_t)buf[2] << 8) + | (uint32_t)buf[3]; +} +#endif + +#ifndef put_unaligned_le32 +static inline void XZ_FUNC put_unaligned_le32(uint32_t val, uint8_t *buf) +{ + buf[0] = (uint8_t)val; + buf[1] = (uint8_t)(val >> 8); + buf[2] = (uint8_t)(val >> 16); + buf[3] = (uint8_t)(val >> 24); +} +#endif + +#ifndef put_unaligned_be32 +static inline void XZ_FUNC put_unaligned_be32(uint32_t val, uint8_t *buf) +{ + buf[0] = (uint8_t)(val >> 24); + buf[1] = (uint8_t)(val >> 16); + buf[2] = (uint8_t)(val >> 8); + buf[3] = (uint8_t)val; +} +#endif + +/* + * Use get_unaligned_le32() also for aligned access for simplicity. On + * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr)) + * could save a few bytes in code size. + */ +#ifndef get_le32 +# define get_le32 get_unaligned_le32 +#endif + +#endif diff --git a/busybox/archival/libarchive/unxz/xz_dec_bcj.c b/busybox/archival/libarchive/unxz/xz_dec_bcj.c new file mode 100644 index 00000000000000..e0f913a9469a5c --- /dev/null +++ b/busybox/archival/libarchive/unxz/xz_dec_bcj.c @@ -0,0 +1,580 @@ +/* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" + +/* + * The rest of the file is inside this ifdef. It makes things a little more + * convenient when building without support for any BCJ filters. + */ +#ifdef XZ_DEC_BCJ + +struct xz_dec_bcj { + /* Type of the BCJ filter being used */ + enum { + BCJ_X86 = 4, /* x86 or x86-64 */ + BCJ_POWERPC = 5, /* Big endian only */ + BCJ_IA64 = 6, /* Big or little endian */ + BCJ_ARM = 7, /* Little endian only */ + BCJ_ARMTHUMB = 8, /* Little endian only */ + BCJ_SPARC = 9 /* Big or little endian */ + } type; + + /* + * Return value of the next filter in the chain. We need to preserve + * this information across calls, because we must not call the next + * filter anymore once it has returned XZ_STREAM_END. + */ + enum xz_ret ret; + + /* True if we are operating in single-call mode. */ + bool single_call; + + /* + * Absolute position relative to the beginning of the uncompressed + * data (in a single .xz Block). We care only about the lowest 32 + * bits so this doesn't need to be uint64_t even with big files. + */ + uint32_t pos; + + /* x86 filter state */ + uint32_t x86_prev_mask; + + /* Temporary space to hold the variables from struct xz_buf */ + uint8_t *out; + size_t out_pos; + size_t out_size; + + struct { + /* Amount of already filtered data in the beginning of buf */ + size_t filtered; + + /* Total amount of data currently stored in buf */ + size_t size; + + /* + * Buffer to hold a mix of filtered and unfiltered data. This + * needs to be big enough to hold Alignment + 2 * Look-ahead: + * + * Type Alignment Look-ahead + * x86 1 4 + * PowerPC 4 0 + * IA-64 16 0 + * ARM 4 0 + * ARM-Thumb 2 2 + * SPARC 4 0 + */ + uint8_t buf[16]; + } temp; +}; + +#ifdef XZ_DEC_X86 +/* + * This is used to test the most significant byte of a memory address + * in an x86 instruction. + */ +static inline int bcj_x86_test_msbyte(uint8_t b) +{ + return b == 0x00 || b == 0xFF; +} + +static noinline_for_stack size_t XZ_FUNC bcj_x86( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + static const bool mask_to_allowed_status[8] + = { true, true, true, false, true, false, false, false }; + + static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 }; + + size_t i; + size_t prev_pos = (size_t)-1; + uint32_t prev_mask = s->x86_prev_mask; + uint32_t src; + uint32_t dest; + uint32_t j; + uint8_t b; + + if (size <= 4) + return 0; + + size -= 4; + for (i = 0; i < size; ++i) { + if ((buf[i] & 0xFE) != 0xE8) + continue; + + prev_pos = i - prev_pos; + if (prev_pos > 3) { + prev_mask = 0; + } else { + prev_mask = (prev_mask << (prev_pos - 1)) & 7; + if (prev_mask != 0) { + b = buf[i + 4 - mask_to_bit_num[prev_mask]]; + if (!mask_to_allowed_status[prev_mask] + || bcj_x86_test_msbyte(b)) { + prev_pos = i; + prev_mask = (prev_mask << 1) | 1; + continue; + } + } + } + + prev_pos = i; + + if (bcj_x86_test_msbyte(buf[i + 4])) { + src = get_unaligned_le32(buf + i + 1); + while (true) { + dest = src - (s->pos + (uint32_t)i + 5); + if (prev_mask == 0) + break; + + j = mask_to_bit_num[prev_mask] * 8; + b = (uint8_t)(dest >> (24 - j)); + if (!bcj_x86_test_msbyte(b)) + break; + + src = dest ^ (((uint32_t)1 << (32 - j)) - 1); + } + + dest &= 0x01FFFFFF; + dest |= (uint32_t)0 - (dest & 0x01000000); + put_unaligned_le32(dest, buf + i + 1); + i += 4; + } else { + prev_mask = (prev_mask << 1) | 1; + } + } + + prev_pos = i - prev_pos; + s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1); + return i; +} +#endif + +#ifdef XZ_DEC_POWERPC +static noinline_for_stack size_t XZ_FUNC bcj_powerpc( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t instr; + + for (i = 0; i + 4 <= size; i += 4) { + instr = get_unaligned_be32(buf + i); + if ((instr & 0xFC000003) == 0x48000001) { + instr &= 0x03FFFFFC; + instr -= s->pos + (uint32_t)i; + instr &= 0x03FFFFFC; + instr |= 0x48000001; + put_unaligned_be32(instr, buf + i); + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_IA64 +static noinline_for_stack size_t XZ_FUNC bcj_ia64( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + static const uint8_t branch_table[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 6, 6, 0, 0, 7, 7, + 4, 4, 0, 0, 4, 4, 0, 0 + }; + + /* + * The local variables take a little bit stack space, but it's less + * than what LZMA2 decoder takes, so it doesn't make sense to reduce + * stack usage here without doing that for the LZMA2 decoder too. + */ + + /* Loop counters */ + size_t i; + size_t j; + + /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */ + uint32_t slot; + + /* Bitwise offset of the instruction indicated by slot */ + uint32_t bit_pos; + + /* bit_pos split into byte and bit parts */ + uint32_t byte_pos; + uint32_t bit_res; + + /* Address part of an instruction */ + uint32_t addr; + + /* Mask used to detect which instructions to convert */ + uint32_t mask; + + /* 41-bit instruction stored somewhere in the lowest 48 bits */ + uint64_t instr; + + /* Instruction normalized with bit_res for easier manipulation */ + uint64_t norm; + + for (i = 0; i + 16 <= size; i += 16) { + mask = branch_table[buf[i] & 0x1F]; + for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) { + if (((mask >> slot) & 1) == 0) + continue; + + byte_pos = bit_pos >> 3; + bit_res = bit_pos & 7; + instr = 0; + for (j = 0; j < 6; ++j) + instr |= (uint64_t)(buf[i + j + byte_pos]) + << (8 * j); + + norm = instr >> bit_res; + + if (((norm >> 37) & 0x0F) == 0x05 + && ((norm >> 9) & 0x07) == 0) { + addr = (norm >> 13) & 0x0FFFFF; + addr |= ((uint32_t)(norm >> 36) & 1) << 20; + addr <<= 4; + addr -= s->pos + (uint32_t)i; + addr >>= 4; + + norm &= ~((uint64_t)0x8FFFFF << 13); + norm |= (uint64_t)(addr & 0x0FFFFF) << 13; + norm |= (uint64_t)(addr & 0x100000) + << (36 - 20); + + instr &= (1 << bit_res) - 1; + instr |= norm << bit_res; + + for (j = 0; j < 6; j++) + buf[i + j + byte_pos] + = (uint8_t)(instr >> (8 * j)); + } + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_ARM +static noinline_for_stack size_t XZ_FUNC bcj_arm( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 4) { + if (buf[i + 3] == 0xEB) { + addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) + | ((uint32_t)buf[i + 2] << 16); + addr <<= 2; + addr -= s->pos + (uint32_t)i + 8; + addr >>= 2; + buf[i] = (uint8_t)addr; + buf[i + 1] = (uint8_t)(addr >> 8); + buf[i + 2] = (uint8_t)(addr >> 16); + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_ARMTHUMB +static noinline_for_stack size_t XZ_FUNC bcj_armthumb( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 2) { + if ((buf[i + 1] & 0xF8) == 0xF0 + && (buf[i + 3] & 0xF8) == 0xF8) { + addr = (((uint32_t)buf[i + 1] & 0x07) << 19) + | ((uint32_t)buf[i] << 11) + | (((uint32_t)buf[i + 3] & 0x07) << 8) + | (uint32_t)buf[i + 2]; + addr <<= 1; + addr -= s->pos + (uint32_t)i + 4; + addr >>= 1; + buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07)); + buf[i] = (uint8_t)(addr >> 11); + buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07)); + buf[i + 2] = (uint8_t)addr; + i += 2; + } + } + + return i; +} +#endif + +#ifdef XZ_DEC_SPARC +static noinline_for_stack size_t XZ_FUNC bcj_sparc( + struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t instr; + + for (i = 0; i + 4 <= size; i += 4) { + instr = get_unaligned_be32(buf + i); + if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) { + instr <<= 2; + instr -= s->pos + (uint32_t)i; + instr >>= 2; + instr = ((uint32_t)0x40000000 - (instr & 0x400000)) + | 0x40000000 | (instr & 0x3FFFFF); + put_unaligned_be32(instr, buf + i); + } + } + + return i; +} +#endif + +/* + * Apply the selected BCJ filter. Update *pos and s->pos to match the amount + * of data that got filtered. + * + * NOTE: This is implemented as a switch statement to avoid using function + * pointers, which could be problematic in the kernel boot code, which must + * avoid pointers to static data (at least on x86). + */ +static void XZ_FUNC bcj_apply(struct xz_dec_bcj *s, + uint8_t *buf, size_t *pos, size_t size) +{ + size_t filtered; + + buf += *pos; + size -= *pos; + + switch (s->type) { +#ifdef XZ_DEC_X86 + case BCJ_X86: + filtered = bcj_x86(s, buf, size); + break; +#endif +#ifdef XZ_DEC_POWERPC + case BCJ_POWERPC: + filtered = bcj_powerpc(s, buf, size); + break; +#endif +#ifdef XZ_DEC_IA64 + case BCJ_IA64: + filtered = bcj_ia64(s, buf, size); + break; +#endif +#ifdef XZ_DEC_ARM + case BCJ_ARM: + filtered = bcj_arm(s, buf, size); + break; +#endif +#ifdef XZ_DEC_ARMTHUMB + case BCJ_ARMTHUMB: + filtered = bcj_armthumb(s, buf, size); + break; +#endif +#ifdef XZ_DEC_SPARC + case BCJ_SPARC: + filtered = bcj_sparc(s, buf, size); + break; +#endif + default: + /* Never reached but silence compiler warnings. */ + filtered = 0; + break; + } + + *pos += filtered; + s->pos += filtered; +} + +/* + * Flush pending filtered data from temp to the output buffer. + * Move the remaining mixture of possibly filtered and unfiltered + * data to the beginning of temp. + */ +static void XZ_FUNC bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b) +{ + size_t copy_size; + + copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos); + memcpy(b->out + b->out_pos, s->temp.buf, copy_size); + b->out_pos += copy_size; + + s->temp.filtered -= copy_size; + s->temp.size -= copy_size; + memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size); +} + +/* + * The BCJ filter functions are primitive in sense that they process the + * data in chunks of 1-16 bytes. To hide this issue, this function does + * some buffering. + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_run(struct xz_dec_bcj *s, + struct xz_dec_lzma2 *lzma2, struct xz_buf *b) +{ + size_t out_start; + + /* + * Flush pending already filtered data to the output buffer. Return + * immediatelly if we couldn't flush everything, or if the next + * filter in the chain had already returned XZ_STREAM_END. + */ + if (s->temp.filtered > 0) { + bcj_flush(s, b); + if (s->temp.filtered > 0) + return XZ_OK; + + if (s->ret == XZ_STREAM_END) + return XZ_STREAM_END; + } + + /* + * If we have more output space than what is currently pending in + * temp, copy the unfiltered data from temp to the output buffer + * and try to fill the output buffer by decoding more data from the + * next filter in the chain. Apply the BCJ filter on the new data + * in the output buffer. If everything cannot be filtered, copy it + * to temp and rewind the output buffer position accordingly. + * + * This needs to be always run when temp.size == 0 to handle a special + * case where the output buffer is full and the next filter has no + * more output coming but hasn't returned XZ_STREAM_END yet. + */ + if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) { + out_start = b->out_pos; + memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size); + b->out_pos += s->temp.size; + + s->ret = xz_dec_lzma2_run(lzma2, b); + if (s->ret != XZ_STREAM_END + && (s->ret != XZ_OK || s->single_call)) + return s->ret; + + bcj_apply(s, b->out, &out_start, b->out_pos); + + /* + * As an exception, if the next filter returned XZ_STREAM_END, + * we can do that too, since the last few bytes that remain + * unfiltered are meant to remain unfiltered. + */ + if (s->ret == XZ_STREAM_END) + return XZ_STREAM_END; + + s->temp.size = b->out_pos - out_start; + b->out_pos -= s->temp.size; + memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size); + + /* + * If there wasn't enough input to the next filter to fill + * the output buffer with unfiltered data, there's no point + * to try decoding more data to temp. + */ + if (b->out_pos + s->temp.size < b->out_size) + return XZ_OK; + } + + /* + * We have unfiltered data in temp. If the output buffer isn't full + * yet, try to fill the temp buffer by decoding more data from the + * next filter. Apply the BCJ filter on temp. Then we hopefully can + * fill the actual output buffer by copying filtered data from temp. + * A mix of filtered and unfiltered data may be left in temp; it will + * be taken care on the next call to this function. + */ + if (b->out_pos < b->out_size) { + /* Make b->out{,_pos,_size} temporarily point to s->temp. */ + s->out = b->out; + s->out_pos = b->out_pos; + s->out_size = b->out_size; + b->out = s->temp.buf; + b->out_pos = s->temp.size; + b->out_size = sizeof(s->temp.buf); + + s->ret = xz_dec_lzma2_run(lzma2, b); + + s->temp.size = b->out_pos; + b->out = s->out; + b->out_pos = s->out_pos; + b->out_size = s->out_size; + + if (s->ret != XZ_OK && s->ret != XZ_STREAM_END) + return s->ret; + + bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size); + + /* + * If the next filter returned XZ_STREAM_END, we mark that + * everything is filtered, since the last unfiltered bytes + * of the stream are meant to be left as is. + */ + if (s->ret == XZ_STREAM_END) + s->temp.filtered = s->temp.size; + + bcj_flush(s, b); + if (s->temp.filtered > 0) + return XZ_OK; + } + + return s->ret; +} + +XZ_EXTERN struct xz_dec_bcj * XZ_FUNC xz_dec_bcj_create(bool single_call) +{ + struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s != NULL) + s->single_call = single_call; + + return s; +} + +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_reset( + struct xz_dec_bcj *s, uint8_t id) +{ + switch (id) { +#ifdef XZ_DEC_X86 + case BCJ_X86: +#endif +#ifdef XZ_DEC_POWERPC + case BCJ_POWERPC: +#endif +#ifdef XZ_DEC_IA64 + case BCJ_IA64: +#endif +#ifdef XZ_DEC_ARM + case BCJ_ARM: +#endif +#ifdef XZ_DEC_ARMTHUMB + case BCJ_ARMTHUMB: +#endif +#ifdef XZ_DEC_SPARC + case BCJ_SPARC: +#endif + break; + + default: + /* Unsupported Filter ID */ + return XZ_OPTIONS_ERROR; + } + + s->type = id; + s->ret = XZ_OK; + s->pos = 0; + s->x86_prev_mask = 0; + s->temp.filtered = 0; + s->temp.size = 0; + + return XZ_OK; +} + +#endif diff --git a/busybox/archival/libarchive/unxz/xz_dec_lzma2.c b/busybox/archival/libarchive/unxz/xz_dec_lzma2.c new file mode 100644 index 00000000000000..bca41e705373a4 --- /dev/null +++ b/busybox/archival/libarchive/unxz/xz_dec_lzma2.c @@ -0,0 +1,1171 @@ +/* + * LZMA2 decoder + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" +#include "xz_lzma2.h" + +/* + * Range decoder initialization eats the first five bytes of each LZMA chunk. + */ +#define RC_INIT_BYTES 5 + +/* + * Minimum number of usable input buffer to safely decode one LZMA symbol. + * The worst case is that we decode 22 bits using probabilities and 26 + * direct bits. This may decode at maximum of 20 bytes of input. However, + * lzma_main() does an extra normalization before returning, thus we + * need to put 21 here. + */ +#define LZMA_IN_REQUIRED 21 + +/* + * Dictionary (history buffer) + * + * These are always true: + * start <= pos <= full <= end + * pos <= limit <= end + * + * In multi-call mode, also these are true: + * end == size + * size <= size_max + * allocated <= size + * + * Most of these variables are size_t to support single-call mode, + * in which the dictionary variables address the actual output + * buffer directly. + */ +struct dictionary { + /* Beginning of the history buffer */ + uint8_t *buf; + + /* Old position in buf (before decoding more data) */ + size_t start; + + /* Position in buf */ + size_t pos; + + /* + * How full dictionary is. This is used to detect corrupt input that + * would read beyond the beginning of the uncompressed stream. + */ + size_t full; + + /* Write limit; we don't write to buf[limit] or later bytes. */ + size_t limit; + + /* + * End of the dictionary buffer. In multi-call mode, this is + * the same as the dictionary size. In single-call mode, this + * indicates the size of the output buffer. + */ + size_t end; + + /* + * Size of the dictionary as specified in Block Header. This is used + * together with "full" to detect corrupt input that would make us + * read beyond the beginning of the uncompressed stream. + */ + uint32_t size; + + /* + * Maximum allowed dictionary size in multi-call mode. + * This is ignored in single-call mode. + */ + uint32_t size_max; + + /* + * Amount of memory currently allocated for the dictionary. + * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC, + * size_max is always the same as the allocated size.) + */ + uint32_t allocated; + + /* Operation mode */ + enum xz_mode mode; +}; + +/* Range decoder */ +struct rc_dec { + uint32_t range; + uint32_t code; + + /* + * Number of initializing bytes remaining to be read + * by rc_read_init(). + */ + uint32_t init_bytes_left; + + /* + * Buffer from which we read our input. It can be either + * temp.buf or the caller-provided input buffer. + */ + const uint8_t *in; + size_t in_pos; + size_t in_limit; +}; + +/* Probabilities for a length decoder. */ +struct lzma_len_dec { + /* Probability of match length being at least 10 */ + uint16_t choice; + + /* Probability of match length being at least 18 */ + uint16_t choice2; + + /* Probabilities for match lengths 2-9 */ + uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS]; + + /* Probabilities for match lengths 10-17 */ + uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS]; + + /* Probabilities for match lengths 18-273 */ + uint16_t high[LEN_HIGH_SYMBOLS]; +}; + +struct lzma_dec { + /* Distances of latest four matches */ + uint32_t rep0; + uint32_t rep1; + uint32_t rep2; + uint32_t rep3; + + /* Types of the most recently seen LZMA symbols */ + enum lzma_state state; + + /* + * Length of a match. This is updated so that dict_repeat can + * be called again to finish repeating the whole match. + */ + uint32_t len; + + /* + * LZMA properties or related bit masks (number of literal + * context bits, a mask dervied from the number of literal + * position bits, and a mask dervied from the number + * position bits) + */ + uint32_t lc; + uint32_t literal_pos_mask; /* (1 << lp) - 1 */ + uint32_t pos_mask; /* (1 << pb) - 1 */ + + /* If 1, it's a match. Otherwise it's a single 8-bit literal. */ + uint16_t is_match[STATES][POS_STATES_MAX]; + + /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */ + uint16_t is_rep[STATES]; + + /* + * If 0, distance of a repeated match is rep0. + * Otherwise check is_rep1. + */ + uint16_t is_rep0[STATES]; + + /* + * If 0, distance of a repeated match is rep1. + * Otherwise check is_rep2. + */ + uint16_t is_rep1[STATES]; + + /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */ + uint16_t is_rep2[STATES]; + + /* + * If 1, the repeated match has length of one byte. Otherwise + * the length is decoded from rep_len_decoder. + */ + uint16_t is_rep0_long[STATES][POS_STATES_MAX]; + + /* + * Probability tree for the highest two bits of the match + * distance. There is a separate probability tree for match + * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273]. + */ + uint16_t dist_slot[DIST_STATES][DIST_SLOTS]; + + /* + * Probility trees for additional bits for match distance + * when the distance is in the range [4, 127]. + */ + uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END]; + + /* + * Probability tree for the lowest four bits of a match + * distance that is equal to or greater than 128. + */ + uint16_t dist_align[ALIGN_SIZE]; + + /* Length of a normal match */ + struct lzma_len_dec match_len_dec; + + /* Length of a repeated match */ + struct lzma_len_dec rep_len_dec; + + /* Probabilities of literals */ + uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE]; +}; + +struct lzma2_dec { + /* Position in xz_dec_lzma2_run(). */ + enum lzma2_seq { + SEQ_CONTROL, + SEQ_UNCOMPRESSED_1, + SEQ_UNCOMPRESSED_2, + SEQ_COMPRESSED_0, + SEQ_COMPRESSED_1, + SEQ_PROPERTIES, + SEQ_LZMA_PREPARE, + SEQ_LZMA_RUN, + SEQ_COPY + } sequence; + + /* Next position after decoding the compressed size of the chunk. */ + enum lzma2_seq next_sequence; + + /* Uncompressed size of LZMA chunk (2 MiB at maximum) */ + uint32_t uncompressed; + + /* + * Compressed size of LZMA chunk or compressed/uncompressed + * size of uncompressed chunk (64 KiB at maximum) + */ + uint32_t compressed; + + /* + * True if dictionary reset is needed. This is false before + * the first chunk (LZMA or uncompressed). + */ + bool need_dict_reset; + + /* + * True if new LZMA properties are needed. This is false + * before the first LZMA chunk. + */ + bool need_props; +}; + +struct xz_dec_lzma2 { + /* + * The order below is important on x86 to reduce code size and + * it shouldn't hurt on other platforms. Everything up to and + * including lzma.pos_mask are in the first 128 bytes on x86-32, + * which allows using smaller instructions to access those + * variables. On x86-64, fewer variables fit into the first 128 + * bytes, but this is still the best order without sacrificing + * the readability by splitting the structures. + */ + struct rc_dec rc; + struct dictionary dict; + struct lzma2_dec lzma2; + struct lzma_dec lzma; + + /* + * Temporary buffer which holds small number of input bytes between + * decoder calls. See lzma2_lzma() for details. + */ + struct { + uint32_t size; + uint8_t buf[3 * LZMA_IN_REQUIRED]; + } temp; +}; + +/************** + * Dictionary * + **************/ + +/* + * Reset the dictionary state. When in single-call mode, set up the beginning + * of the dictionary to point to the actual output buffer. + */ +static void XZ_FUNC dict_reset(struct dictionary *dict, struct xz_buf *b) +{ + if (DEC_IS_SINGLE(dict->mode)) { + dict->buf = b->out + b->out_pos; + dict->end = b->out_size - b->out_pos; + } + + dict->start = 0; + dict->pos = 0; + dict->limit = 0; + dict->full = 0; +} + +/* Set dictionary write limit */ +static void XZ_FUNC dict_limit(struct dictionary *dict, size_t out_max) +{ + if (dict->end - dict->pos <= out_max) + dict->limit = dict->end; + else + dict->limit = dict->pos + out_max; +} + +/* Return true if at least one byte can be written into the dictionary. */ +static __always_inline bool XZ_FUNC dict_has_space(const struct dictionary *dict) +{ + return dict->pos < dict->limit; +} + +/* + * Get a byte from the dictionary at the given distance. The distance is + * assumed to valid, or as a special case, zero when the dictionary is + * still empty. This special case is needed for single-call decoding to + * avoid writing a '\0' to the end of the destination buffer. + */ +static __always_inline uint32_t XZ_FUNC dict_get( + const struct dictionary *dict, uint32_t dist) +{ + size_t offset = dict->pos - dist - 1; + + if (dist >= dict->pos) + offset += dict->end; + + return dict->full > 0 ? dict->buf[offset] : 0; +} + +/* + * Put one byte into the dictionary. It is assumed that there is space for it. + */ +static inline void XZ_FUNC dict_put(struct dictionary *dict, uint8_t byte) +{ + dict->buf[dict->pos++] = byte; + + if (dict->full < dict->pos) + dict->full = dict->pos; +} + +/* + * Repeat given number of bytes from the given distance. If the distance is + * invalid, false is returned. On success, true is returned and *len is + * updated to indicate how many bytes were left to be repeated. + */ +static bool XZ_FUNC dict_repeat( + struct dictionary *dict, uint32_t *len, uint32_t dist) +{ + size_t back; + uint32_t left; + + if (dist >= dict->full || dist >= dict->size) + return false; + + left = min_t(size_t, dict->limit - dict->pos, *len); + *len -= left; + + back = dict->pos - dist - 1; + if (dist >= dict->pos) + back += dict->end; + + do { + dict->buf[dict->pos++] = dict->buf[back++]; + if (back == dict->end) + back = 0; + } while (--left > 0); + + if (dict->full < dict->pos) + dict->full = dict->pos; + + return true; +} + +/* Copy uncompressed data as is from input to dictionary and output buffers. */ +static void XZ_FUNC dict_uncompressed( + struct dictionary *dict, struct xz_buf *b, uint32_t *left) +{ + size_t copy_size; + + while (*left > 0 && b->in_pos < b->in_size + && b->out_pos < b->out_size) { + copy_size = min(b->in_size - b->in_pos, + b->out_size - b->out_pos); + if (copy_size > dict->end - dict->pos) + copy_size = dict->end - dict->pos; + if (copy_size > *left) + copy_size = *left; + + *left -= copy_size; + + memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size); + dict->pos += copy_size; + + if (dict->full < dict->pos) + dict->full = dict->pos; + + if (DEC_IS_MULTI(dict->mode)) { + if (dict->pos == dict->end) + dict->pos = 0; + + memcpy(b->out + b->out_pos, b->in + b->in_pos, + copy_size); + } + + dict->start = dict->pos; + + b->out_pos += copy_size; + b->in_pos += copy_size; + } +} + +/* + * Flush pending data from dictionary to b->out. It is assumed that there is + * enough space in b->out. This is guaranteed because caller uses dict_limit() + * before decoding data into the dictionary. + */ +static uint32_t XZ_FUNC dict_flush(struct dictionary *dict, struct xz_buf *b) +{ + size_t copy_size = dict->pos - dict->start; + + if (DEC_IS_MULTI(dict->mode)) { + if (dict->pos == dict->end) + dict->pos = 0; + + memcpy(b->out + b->out_pos, dict->buf + dict->start, + copy_size); + } + + dict->start = dict->pos; + b->out_pos += copy_size; + return copy_size; +} + +/***************** + * Range decoder * + *****************/ + +/* Reset the range decoder. */ +static void XZ_FUNC rc_reset(struct rc_dec *rc) +{ + rc->range = (uint32_t)-1; + rc->code = 0; + rc->init_bytes_left = RC_INIT_BYTES; +} + +/* + * Read the first five initial bytes into rc->code if they haven't been + * read already. (Yes, the first byte gets completely ignored.) + */ +static bool XZ_FUNC rc_read_init(struct rc_dec *rc, struct xz_buf *b) +{ + while (rc->init_bytes_left > 0) { + if (b->in_pos == b->in_size) + return false; + + rc->code = (rc->code << 8) + b->in[b->in_pos++]; + --rc->init_bytes_left; + } + + return true; +} + +/* Return true if there may not be enough input for the next decoding loop. */ +static inline bool XZ_FUNC rc_limit_exceeded(const struct rc_dec *rc) +{ + return rc->in_pos > rc->in_limit; +} + +/* + * Return true if it is possible (from point of view of range decoder) that + * we have reached the end of the LZMA chunk. + */ +static inline bool XZ_FUNC rc_is_finished(const struct rc_dec *rc) +{ + return rc->code == 0; +} + +/* Read the next input byte if needed. */ +static __always_inline void XZ_FUNC rc_normalize(struct rc_dec *rc) +{ + if (rc->range < RC_TOP_VALUE) { + rc->range <<= RC_SHIFT_BITS; + rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++]; + } +} + +/* + * Decode one bit. In some versions, this function has been split in three + * functions so that the compiler is supposed to be able to more easily avoid + * an extra branch. In this particular version of the LZMA decoder, this + * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3 + * on x86). Using a non-split version results in nicer looking code too. + * + * NOTE: This must return an int. Do not make it return a bool or the speed + * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care, + * and it generates 10-20 % faster code than GCC 3.x from this file anyway.) + */ +static __always_inline int XZ_FUNC rc_bit(struct rc_dec *rc, uint16_t *prob) +{ + uint32_t bound; + int bit; + + rc_normalize(rc); + bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob; + if (rc->code < bound) { + rc->range = bound; + *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS; + bit = 0; + } else { + rc->range -= bound; + rc->code -= bound; + *prob -= *prob >> RC_MOVE_BITS; + bit = 1; + } + + return bit; +} + +/* Decode a bittree starting from the most significant bit. */ +static __always_inline uint32_t XZ_FUNC rc_bittree( + struct rc_dec *rc, uint16_t *probs, uint32_t limit) +{ + uint32_t symbol = 1; + + do { + if (rc_bit(rc, &probs[symbol])) + symbol = (symbol << 1) + 1; + else + symbol <<= 1; + } while (symbol < limit); + + return symbol; +} + +/* Decode a bittree starting from the least significant bit. */ +static __always_inline void XZ_FUNC rc_bittree_reverse(struct rc_dec *rc, + uint16_t *probs, uint32_t *dest, uint32_t limit) +{ + uint32_t symbol = 1; + uint32_t i = 0; + + do { + if (rc_bit(rc, &probs[symbol])) { + symbol = (symbol << 1) + 1; + *dest += 1 << i; + } else { + symbol <<= 1; + } + } while (++i < limit); +} + +/* Decode direct bits (fixed fifty-fifty probability) */ +static inline void XZ_FUNC rc_direct( + struct rc_dec *rc, uint32_t *dest, uint32_t limit) +{ + uint32_t mask; + + do { + rc_normalize(rc); + rc->range >>= 1; + rc->code -= rc->range; + mask = (uint32_t)0 - (rc->code >> 31); + rc->code += rc->range & mask; + *dest = (*dest << 1) + (mask + 1); + } while (--limit > 0); +} + +/******** + * LZMA * + ********/ + +/* Get pointer to literal coder probability array. */ +static uint16_t * XZ_FUNC lzma_literal_probs(struct xz_dec_lzma2 *s) +{ + uint32_t prev_byte = dict_get(&s->dict, 0); + uint32_t low = prev_byte >> (8 - s->lzma.lc); + uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc; + return s->lzma.literal[low + high]; +} + +/* Decode a literal (one 8-bit byte) */ +static void XZ_FUNC lzma_literal(struct xz_dec_lzma2 *s) +{ + uint16_t *probs; + uint32_t symbol; + uint32_t match_byte; + uint32_t match_bit; + uint32_t offset; + uint32_t i; + + probs = lzma_literal_probs(s); + + if (lzma_state_is_literal(s->lzma.state)) { + symbol = rc_bittree(&s->rc, probs, 0x100); + } else { + symbol = 1; + match_byte = dict_get(&s->dict, s->lzma.rep0) << 1; + offset = 0x100; + + do { + match_bit = match_byte & offset; + match_byte <<= 1; + i = offset + match_bit + symbol; + + if (rc_bit(&s->rc, &probs[i])) { + symbol = (symbol << 1) + 1; + offset &= match_bit; + } else { + symbol <<= 1; + offset &= ~match_bit; + } + } while (symbol < 0x100); + } + + dict_put(&s->dict, (uint8_t)symbol); + lzma_state_literal(&s->lzma.state); +} + +/* Decode the length of the match into s->lzma.len. */ +static void XZ_FUNC lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l, + uint32_t pos_state) +{ + uint16_t *probs; + uint32_t limit; + + if (!rc_bit(&s->rc, &l->choice)) { + probs = l->low[pos_state]; + limit = LEN_LOW_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN; + } else { + if (!rc_bit(&s->rc, &l->choice2)) { + probs = l->mid[pos_state]; + limit = LEN_MID_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS; + } else { + probs = l->high; + limit = LEN_HIGH_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS + + LEN_MID_SYMBOLS; + } + } + + s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit; +} + +/* Decode a match. The distance will be stored in s->lzma.rep0. */ +static void XZ_FUNC lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +{ + uint16_t *probs; + uint32_t dist_slot; + uint32_t limit; + + lzma_state_match(&s->lzma.state); + + s->lzma.rep3 = s->lzma.rep2; + s->lzma.rep2 = s->lzma.rep1; + s->lzma.rep1 = s->lzma.rep0; + + lzma_len(s, &s->lzma.match_len_dec, pos_state); + + probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)]; + dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS; + + if (dist_slot < DIST_MODEL_START) { + s->lzma.rep0 = dist_slot; + } else { + limit = (dist_slot >> 1) - 1; + s->lzma.rep0 = 2 + (dist_slot & 1); + + if (dist_slot < DIST_MODEL_END) { + s->lzma.rep0 <<= limit; + probs = s->lzma.dist_special + s->lzma.rep0 + - dist_slot - 1; + rc_bittree_reverse(&s->rc, probs, + &s->lzma.rep0, limit); + } else { + rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS); + s->lzma.rep0 <<= ALIGN_BITS; + rc_bittree_reverse(&s->rc, s->lzma.dist_align, + &s->lzma.rep0, ALIGN_BITS); + } + } +} + +/* + * Decode a repeated match. The distance is one of the four most recently + * seen matches. The distance will be stored in s->lzma.rep0. + */ +static void XZ_FUNC lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +{ + uint32_t tmp; + + if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) { + if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[ + s->lzma.state][pos_state])) { + lzma_state_short_rep(&s->lzma.state); + s->lzma.len = 1; + return; + } + } else { + if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) { + tmp = s->lzma.rep1; + } else { + if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) { + tmp = s->lzma.rep2; + } else { + tmp = s->lzma.rep3; + s->lzma.rep3 = s->lzma.rep2; + } + + s->lzma.rep2 = s->lzma.rep1; + } + + s->lzma.rep1 = s->lzma.rep0; + s->lzma.rep0 = tmp; + } + + lzma_state_long_rep(&s->lzma.state); + lzma_len(s, &s->lzma.rep_len_dec, pos_state); +} + +/* LZMA decoder core */ +static bool XZ_FUNC lzma_main(struct xz_dec_lzma2 *s) +{ + uint32_t pos_state; + + /* + * If the dictionary was reached during the previous call, try to + * finish the possibly pending repeat in the dictionary. + */ + if (dict_has_space(&s->dict) && s->lzma.len > 0) + dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0); + + /* + * Decode more LZMA symbols. One iteration may consume up to + * LZMA_IN_REQUIRED - 1 bytes. + */ + while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) { + pos_state = s->dict.pos & s->lzma.pos_mask; + + if (!rc_bit(&s->rc, &s->lzma.is_match[ + s->lzma.state][pos_state])) { + lzma_literal(s); + } else { + if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state])) + lzma_rep_match(s, pos_state); + else + lzma_match(s, pos_state); + + if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0)) + return false; + } + } + + /* + * Having the range decoder always normalized when we are outside + * this function makes it easier to correctly handle end of the chunk. + */ + rc_normalize(&s->rc); + + return true; +} + +/* + * Reset the LZMA decoder and range decoder state. Dictionary is nore reset + * here, because LZMA state may be reset without resetting the dictionary. + */ +static void XZ_FUNC lzma_reset(struct xz_dec_lzma2 *s) +{ + uint16_t *probs; + size_t i; + + s->lzma.state = STATE_LIT_LIT; + s->lzma.rep0 = 0; + s->lzma.rep1 = 0; + s->lzma.rep2 = 0; + s->lzma.rep3 = 0; + + /* + * All probabilities are initialized to the same value. This hack + * makes the code smaller by avoiding a separate loop for each + * probability array. + * + * This could be optimized so that only that part of literal + * probabilities that are actually required. In the common case + * we would write 12 KiB less. + */ + probs = s->lzma.is_match[0]; + for (i = 0; i < PROBS_TOTAL; ++i) + probs[i] = RC_BIT_MODEL_TOTAL / 2; + + rc_reset(&s->rc); +} + +/* + * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks + * from the decoded lp and pb values. On success, the LZMA decoder state is + * reset and true is returned. + */ +static bool XZ_FUNC lzma_props(struct xz_dec_lzma2 *s, uint8_t props) +{ + if (props > (4 * 5 + 4) * 9 + 8) + return false; + + s->lzma.pos_mask = 0; + while (props >= 9 * 5) { + props -= 9 * 5; + ++s->lzma.pos_mask; + } + + s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1; + + s->lzma.literal_pos_mask = 0; + while (props >= 9) { + props -= 9; + ++s->lzma.literal_pos_mask; + } + + s->lzma.lc = props; + + if (s->lzma.lc + s->lzma.literal_pos_mask > 4) + return false; + + s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1; + + lzma_reset(s); + + return true; +} + +/********* + * LZMA2 * + *********/ + +/* + * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't + * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This + * wrapper function takes care of making the LZMA decoder's assumption safe. + * + * As long as there is plenty of input left to be decoded in the current LZMA + * chunk, we decode directly from the caller-supplied input buffer until + * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into + * s->temp.buf, which (hopefully) gets filled on the next call to this + * function. We decode a few bytes from the temporary buffer so that we can + * continue decoding from the caller-supplied input buffer again. + */ +static bool XZ_FUNC lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b) +{ + size_t in_avail; + uint32_t tmp; + + in_avail = b->in_size - b->in_pos; + if (s->temp.size > 0 || s->lzma2.compressed == 0) { + tmp = 2 * LZMA_IN_REQUIRED - s->temp.size; + if (tmp > s->lzma2.compressed - s->temp.size) + tmp = s->lzma2.compressed - s->temp.size; + if (tmp > in_avail) + tmp = in_avail; + + memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp); + + if (s->temp.size + tmp == s->lzma2.compressed) { + memzero(s->temp.buf + s->temp.size + tmp, + sizeof(s->temp.buf) + - s->temp.size - tmp); + s->rc.in_limit = s->temp.size + tmp; + } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) { + s->temp.size += tmp; + b->in_pos += tmp; + return true; + } else { + s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED; + } + + s->rc.in = s->temp.buf; + s->rc.in_pos = 0; + + if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp) + return false; + + s->lzma2.compressed -= s->rc.in_pos; + + if (s->rc.in_pos < s->temp.size) { + s->temp.size -= s->rc.in_pos; + memmove(s->temp.buf, s->temp.buf + s->rc.in_pos, + s->temp.size); + return true; + } + + b->in_pos += s->rc.in_pos - s->temp.size; + s->temp.size = 0; + } + + in_avail = b->in_size - b->in_pos; + if (in_avail >= LZMA_IN_REQUIRED) { + s->rc.in = b->in; + s->rc.in_pos = b->in_pos; + + if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED) + s->rc.in_limit = b->in_pos + s->lzma2.compressed; + else + s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED; + + if (!lzma_main(s)) + return false; + + in_avail = s->rc.in_pos - b->in_pos; + if (in_avail > s->lzma2.compressed) + return false; + + s->lzma2.compressed -= in_avail; + b->in_pos = s->rc.in_pos; + } + + in_avail = b->in_size - b->in_pos; + if (in_avail < LZMA_IN_REQUIRED) { + if (in_avail > s->lzma2.compressed) + in_avail = s->lzma2.compressed; + + memcpy(s->temp.buf, b->in + b->in_pos, in_avail); + s->temp.size = in_avail; + b->in_pos += in_avail; + } + + return true; +} + +/* + * Take care of the LZMA2 control layer, and forward the job of actual LZMA + * decoding or copying of uncompressed chunks to other functions. + */ +XZ_EXTERN NOINLINE enum xz_ret XZ_FUNC xz_dec_lzma2_run( + struct xz_dec_lzma2 *s, struct xz_buf *b) +{ + uint32_t tmp; + + while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) { + switch (s->lzma2.sequence) { + case SEQ_CONTROL: + /* + * LZMA2 control byte + * + * Exact values: + * 0x00 End marker + * 0x01 Dictionary reset followed by + * an uncompressed chunk + * 0x02 Uncompressed chunk (no dictionary reset) + * + * Highest three bits (s->control & 0xE0): + * 0xE0 Dictionary reset, new properties and state + * reset, followed by LZMA compressed chunk + * 0xC0 New properties and state reset, followed + * by LZMA compressed chunk (no dictionary + * reset) + * 0xA0 State reset using old properties, + * followed by LZMA compressed chunk (no + * dictionary reset) + * 0x80 LZMA chunk (no dictionary or state reset) + * + * For LZMA compressed chunks, the lowest five bits + * (s->control & 1F) are the highest bits of the + * uncompressed size (bits 16-20). + * + * A new LZMA2 stream must begin with a dictionary + * reset. The first LZMA chunk must set new + * properties and reset the LZMA state. + * + * Values that don't match anything described above + * are invalid and we return XZ_DATA_ERROR. + */ + tmp = b->in[b->in_pos++]; + + if (tmp == 0x00) + return XZ_STREAM_END; + + if (tmp >= 0xE0 || tmp == 0x01) { + s->lzma2.need_props = true; + s->lzma2.need_dict_reset = false; + dict_reset(&s->dict, b); + } else if (s->lzma2.need_dict_reset) { + return XZ_DATA_ERROR; + } + + if (tmp >= 0x80) { + s->lzma2.uncompressed = (tmp & 0x1F) << 16; + s->lzma2.sequence = SEQ_UNCOMPRESSED_1; + + if (tmp >= 0xC0) { + /* + * When there are new properties, + * state reset is done at + * SEQ_PROPERTIES. + */ + 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; + if (tmp >= 0xA0) + lzma_reset(s); + } + } else { + if (tmp > 0x02) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_COMPRESSED_0; + s->lzma2.next_sequence = SEQ_COPY; + } + + break; + + case SEQ_UNCOMPRESSED_1: + s->lzma2.uncompressed + += (uint32_t)b->in[b->in_pos++] << 8; + s->lzma2.sequence = SEQ_UNCOMPRESSED_2; + break; + + case SEQ_UNCOMPRESSED_2: + s->lzma2.uncompressed + += (uint32_t)b->in[b->in_pos++] + 1; + s->lzma2.sequence = SEQ_COMPRESSED_0; + break; + + case SEQ_COMPRESSED_0: + s->lzma2.compressed + = (uint32_t)b->in[b->in_pos++] << 8; + s->lzma2.sequence = SEQ_COMPRESSED_1; + break; + + case SEQ_COMPRESSED_1: + s->lzma2.compressed + += (uint32_t)b->in[b->in_pos++] + 1; + s->lzma2.sequence = s->lzma2.next_sequence; + break; + + case SEQ_PROPERTIES: + if (!lzma_props(s, b->in[b->in_pos++])) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_LZMA_PREPARE; + + case SEQ_LZMA_PREPARE: + if (s->lzma2.compressed < RC_INIT_BYTES) + return XZ_DATA_ERROR; + + if (!rc_read_init(&s->rc, b)) + return XZ_OK; + + s->lzma2.compressed -= RC_INIT_BYTES; + s->lzma2.sequence = SEQ_LZMA_RUN; + + case SEQ_LZMA_RUN: + /* + * Set dictionary limit to indicate how much we want + * to be encoded at maximum. Decode new data into the + * dictionary. Flush the new data from dictionary to + * b->out. Check if we finished decoding this chunk. + * In case the dictionary got full but we didn't fill + * the output buffer yet, we may run this loop + * multiple times without changing s->lzma2.sequence. + */ + dict_limit(&s->dict, min_t(size_t, + b->out_size - b->out_pos, + s->lzma2.uncompressed)); + if (!lzma2_lzma(s, b)) + return XZ_DATA_ERROR; + + s->lzma2.uncompressed -= dict_flush(&s->dict, b); + + if (s->lzma2.uncompressed == 0) { + if (s->lzma2.compressed > 0 || s->lzma.len > 0 + || !rc_is_finished(&s->rc)) + return XZ_DATA_ERROR; + + 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 + < s->lzma2.compressed)) { + return XZ_OK; + } + + break; + + case SEQ_COPY: + dict_uncompressed(&s->dict, b, &s->lzma2.compressed); + if (s->lzma2.compressed > 0) + return XZ_OK; + + s->lzma2.sequence = SEQ_CONTROL; + break; + } + } + + return XZ_OK; +} + +XZ_EXTERN struct xz_dec_lzma2 * XZ_FUNC xz_dec_lzma2_create( + enum xz_mode mode, uint32_t dict_max) +{ + struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->dict.mode = mode; + s->dict.size_max = dict_max; + + if (DEC_IS_PREALLOC(mode)) { + s->dict.buf = vmalloc(dict_max); + if (s->dict.buf == NULL) { + kfree(s); + return NULL; + } + } else if (DEC_IS_DYNALLOC(mode)) { + s->dict.buf = NULL; + s->dict.allocated = 0; + } + + return s; +} + +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_lzma2_reset( + struct xz_dec_lzma2 *s, uint8_t props) +{ + /* This limits dictionary size to 3 GiB to keep parsing simpler. */ + if (props > 39) + return XZ_OPTIONS_ERROR; + + s->dict.size = 2 + (props & 1); + s->dict.size <<= (props >> 1) + 11; + + if (DEC_IS_MULTI(s->dict.mode)) { + if (s->dict.size > s->dict.size_max) + return XZ_MEMLIMIT_ERROR; + + s->dict.end = s->dict.size; + + if (DEC_IS_DYNALLOC(s->dict.mode)) { + if (s->dict.allocated < s->dict.size) { + vfree(s->dict.buf); + s->dict.buf = vmalloc(s->dict.size); + if (s->dict.buf == NULL) { + s->dict.allocated = 0; + return XZ_MEM_ERROR; + } + } + } + } + + s->lzma.len = 0; + + s->lzma2.sequence = SEQ_CONTROL; + s->lzma2.need_dict_reset = true; + + s->temp.size = 0; + + return XZ_OK; +} + +XZ_EXTERN void XZ_FUNC xz_dec_lzma2_end(struct xz_dec_lzma2 *s) +{ + if (DEC_IS_MULTI(s->dict.mode)) + vfree(s->dict.buf); + + kfree(s); +} diff --git a/busybox/archival/libarchive/unxz/xz_dec_stream.c b/busybox/archival/libarchive/unxz/xz_dec_stream.c new file mode 100644 index 00000000000000..bf791055b5723a --- /dev/null +++ b/busybox/archival/libarchive/unxz/xz_dec_stream.c @@ -0,0 +1,820 @@ +/* + * .xz Stream decoder + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" +#include "xz_stream.h" + +/* Hash used to validate the Index field */ +struct xz_dec_hash { + vli_type unpadded; + vli_type uncompressed; + uint32_t crc32; +}; + +struct xz_dec { + /* Position in dec_main() */ + enum { + SEQ_STREAM_HEADER, + SEQ_BLOCK_START, + SEQ_BLOCK_HEADER, + SEQ_BLOCK_UNCOMPRESS, + SEQ_BLOCK_PADDING, + SEQ_BLOCK_CHECK, + SEQ_INDEX, + SEQ_INDEX_PADDING, + SEQ_INDEX_CRC32, + SEQ_STREAM_FOOTER + } sequence; + + /* Position in variable-length integers and Check fields */ + uint32_t pos; + + /* Variable-length integer decoded by dec_vli() */ + vli_type vli; + + /* Saved in_pos and out_pos */ + size_t in_start; + size_t out_start; + + /* CRC32 value in Block or Index */ + uint32_t crc32; + + /* Type of the integrity check calculated from uncompressed data */ + enum xz_check check_type; + + /* Operation mode */ + enum xz_mode mode; + + /* + * True if the next call to xz_dec_run() is allowed to return + * XZ_BUF_ERROR. + */ + bool allow_buf_error; + + /* Information stored in Block Header */ + struct { + /* + * Value stored in the Compressed Size field, or + * VLI_UNKNOWN if Compressed Size is not present. + */ + vli_type compressed; + + /* + * Value stored in the Uncompressed Size field, or + * VLI_UNKNOWN if Uncompressed Size is not present. + */ + vli_type uncompressed; + + /* Size of the Block Header field */ + uint32_t size; + } block_header; + + /* Information collected when decoding Blocks */ + struct { + /* Observed compressed size of the current Block */ + vli_type compressed; + + /* Observed uncompressed size of the current Block */ + vli_type uncompressed; + + /* Number of Blocks decoded so far */ + vli_type count; + + /* + * Hash calculated from the Block sizes. This is used to + * validate the Index field. + */ + struct xz_dec_hash hash; + } block; + + /* Variables needed when verifying the Index field */ + struct { + /* Position in dec_index() */ + enum { + SEQ_INDEX_COUNT, + SEQ_INDEX_UNPADDED, + SEQ_INDEX_UNCOMPRESSED + } sequence; + + /* Size of the Index in bytes */ + vli_type size; + + /* Number of Records (matches block.count in valid files) */ + vli_type count; + + /* + * Hash calculated from the Records (matches block.hash in + * valid files). + */ + struct xz_dec_hash hash; + } index; + + /* + * Temporary buffer needed to hold Stream Header, Block Header, + * and Stream Footer. The Block Header is the biggest (1 KiB) + * so we reserve space according to that. buf[] has to be aligned + * to a multiple of four bytes; the size_t variables before it + * should guarantee this. + */ + struct { + size_t pos; + size_t size; + uint8_t buf[1024]; + } temp; + + struct xz_dec_lzma2 *lzma2; + +#ifdef XZ_DEC_BCJ + struct xz_dec_bcj *bcj; + bool bcj_active; +#endif +}; + +#ifdef XZ_DEC_ANY_CHECK +/* Sizes of the Check field with different Check IDs */ +static const uint8_t check_sizes[16] = { + 0, + 4, 4, 4, + 8, 8, 8, + 16, 16, 16, + 32, 32, 32, + 64, 64, 64 +}; +#endif + +/* + * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller + * must have set s->temp.pos to indicate how much data we are supposed + * to copy into s->temp.buf. Return true once s->temp.pos has reached + * s->temp.size. + */ +static bool XZ_FUNC fill_temp(struct xz_dec *s, struct xz_buf *b) +{ + size_t copy_size = min_t(size_t, + b->in_size - b->in_pos, s->temp.size - s->temp.pos); + + memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size); + b->in_pos += copy_size; + s->temp.pos += copy_size; + + if (s->temp.pos == s->temp.size) { + s->temp.pos = 0; + return true; + } + + return false; +} + +/* Decode a variable-length integer (little-endian base-128 encoding) */ +static enum xz_ret XZ_FUNC dec_vli(struct xz_dec *s, + const uint8_t *in, size_t *in_pos, size_t in_size) +{ + uint8_t byte; + + if (s->pos == 0) + s->vli = 0; + + while (*in_pos < in_size) { + byte = in[*in_pos]; + ++*in_pos; + + s->vli |= (vli_type)(byte & 0x7F) << s->pos; + + if ((byte & 0x80) == 0) { + /* Don't allow non-minimal encodings. */ + if (byte == 0 && s->pos != 0) + return XZ_DATA_ERROR; + + s->pos = 0; + return XZ_STREAM_END; + } + + s->pos += 7; + if (s->pos == 7 * VLI_BYTES_MAX) + return XZ_DATA_ERROR; + } + + return XZ_OK; +} + +/* + * Decode the Compressed Data field from a Block. Update and validate + * the observed compressed and uncompressed sizes of the Block so that + * they don't exceed the values possibly stored in the Block Header + * (validation assumes that no integer overflow occurs, since vli_type + * is normally uint64_t). Update the CRC32 if presence of the CRC32 + * field was indicated in Stream Header. + * + * Once the decoding is finished, validate that the observed sizes match + * the sizes possibly stored in the Block Header. Update the hash and + * Block count, which are later used to validate the Index field. + */ +static enum xz_ret XZ_FUNC dec_block(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + s->in_start = b->in_pos; + s->out_start = b->out_pos; + +#ifdef XZ_DEC_BCJ + if (s->bcj_active) + ret = xz_dec_bcj_run(s->bcj, s->lzma2, b); + else +#endif + ret = xz_dec_lzma2_run(s->lzma2, b); + + s->block.compressed += b->in_pos - s->in_start; + s->block.uncompressed += b->out_pos - s->out_start; + + /* + * There is no need to separately check for VLI_UNKNOWN, since + * the observed sizes are always smaller than VLI_UNKNOWN. + */ + if (s->block.compressed > s->block_header.compressed + || s->block.uncompressed + > s->block_header.uncompressed) + return XZ_DATA_ERROR; + + if (s->check_type == XZ_CHECK_CRC32) + s->crc32 = xz_crc32(b->out + s->out_start, + b->out_pos - s->out_start, s->crc32); + + if (ret == XZ_STREAM_END) { + if (s->block_header.compressed != VLI_UNKNOWN + && s->block_header.compressed + != s->block.compressed) + return XZ_DATA_ERROR; + + if (s->block_header.uncompressed != VLI_UNKNOWN + && s->block_header.uncompressed + != s->block.uncompressed) + return XZ_DATA_ERROR; + + s->block.hash.unpadded += s->block_header.size + + s->block.compressed; + +#ifdef XZ_DEC_ANY_CHECK + s->block.hash.unpadded += check_sizes[s->check_type]; +#else + if (s->check_type == XZ_CHECK_CRC32) + s->block.hash.unpadded += 4; +#endif + + s->block.hash.uncompressed += s->block.uncompressed; + s->block.hash.crc32 = xz_crc32( + (const uint8_t *)&s->block.hash, + sizeof(s->block.hash), s->block.hash.crc32); + + ++s->block.count; + } + + return ret; +} + +/* Update the Index size and the CRC32 value. */ +static void XZ_FUNC index_update(struct xz_dec *s, const struct xz_buf *b) +{ + size_t in_used = b->in_pos - s->in_start; + s->index.size += in_used; + s->crc32 = xz_crc32(b->in + s->in_start, in_used, s->crc32); +} + +/* + * Decode the Number of Records, Unpadded Size, and Uncompressed Size + * fields from the Index field. That is, Index Padding and CRC32 are not + * decoded by this function. + * + * This can return XZ_OK (more input needed), XZ_STREAM_END (everything + * successfully decoded), or XZ_DATA_ERROR (input is corrupt). + */ +static enum xz_ret XZ_FUNC dec_index(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + do { + ret = dec_vli(s, b->in, &b->in_pos, b->in_size); + if (ret != XZ_STREAM_END) { + index_update(s, b); + return ret; + } + + switch (s->index.sequence) { + case SEQ_INDEX_COUNT: + s->index.count = s->vli; + + /* + * Validate that the Number of Records field + * indicates the same number of Records as + * there were Blocks in the Stream. + */ + if (s->index.count != s->block.count) + return XZ_DATA_ERROR; + + s->index.sequence = SEQ_INDEX_UNPADDED; + break; + + case SEQ_INDEX_UNPADDED: + s->index.hash.unpadded += s->vli; + s->index.sequence = SEQ_INDEX_UNCOMPRESSED; + break; + + case SEQ_INDEX_UNCOMPRESSED: + s->index.hash.uncompressed += s->vli; + s->index.hash.crc32 = xz_crc32( + (const uint8_t *)&s->index.hash, + sizeof(s->index.hash), + s->index.hash.crc32); + --s->index.count; + s->index.sequence = SEQ_INDEX_UNPADDED; + break; + } + } while (s->index.count > 0); + + return XZ_STREAM_END; +} + +/* + * Validate that the next four input bytes match the value of s->crc32. + * s->pos must be zero when starting to validate the first byte. + */ +static enum xz_ret XZ_FUNC crc32_validate(struct xz_dec *s, struct xz_buf *b) +{ + do { + if (b->in_pos == b->in_size) + return XZ_OK; + + if (((s->crc32 >> s->pos) & 0xFF) != b->in[b->in_pos++]) + return XZ_DATA_ERROR; + + s->pos += 8; + } while (s->pos < 32); + + s->crc32 = 0; + s->pos = 0; + + return XZ_STREAM_END; +} + +#ifdef XZ_DEC_ANY_CHECK +/* + * Skip over the Check field when the Check ID is not supported. + * Returns true once the whole Check field has been skipped over. + */ +static bool XZ_FUNC check_skip(struct xz_dec *s, struct xz_buf *b) +{ + while (s->pos < check_sizes[s->check_type]) { + if (b->in_pos == b->in_size) + return false; + + ++b->in_pos; + ++s->pos; + } + + s->pos = 0; + + return true; +} +#endif + +/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */ +static enum xz_ret XZ_FUNC dec_stream_header(struct xz_dec *s) +{ + if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE)) + return XZ_FORMAT_ERROR; + + if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0) + != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2)) + return XZ_DATA_ERROR; + + if (s->temp.buf[HEADER_MAGIC_SIZE] != 0) + return XZ_OPTIONS_ERROR; + + /* + * Of integrity checks, we support only none (Check ID = 0) and + * CRC32 (Check ID = 1). However, if XZ_DEC_ANY_CHECK is defined, + * we will accept other check types too, but then the check won't + * be verified and a warning (XZ_UNSUPPORTED_CHECK) will be given. + */ + s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1]; + +#ifdef XZ_DEC_ANY_CHECK + if (s->check_type > XZ_CHECK_MAX) + return XZ_OPTIONS_ERROR; + + if (s->check_type > XZ_CHECK_CRC32) + return XZ_UNSUPPORTED_CHECK; +#else + if (s->check_type > XZ_CHECK_CRC32) + return XZ_OPTIONS_ERROR; +#endif + + return XZ_OK; +} + +/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */ +static enum xz_ret XZ_FUNC dec_stream_footer(struct xz_dec *s) +{ + if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE)) + return XZ_DATA_ERROR; + + if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf)) + return XZ_DATA_ERROR; + + /* + * Validate Backward Size. Note that we never added the size of the + * Index CRC32 field to s->index.size, thus we use s->index.size / 4 + * instead of s->index.size / 4 - 1. + */ + if ((s->index.size >> 2) != get_le32(s->temp.buf + 4)) + return XZ_DATA_ERROR; + + if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type) + return XZ_DATA_ERROR; + + /* + * Use XZ_STREAM_END instead of XZ_OK to be more convenient + * for the caller. + */ + return XZ_STREAM_END; +} + +/* Decode the Block Header and initialize the filter chain. */ +static enum xz_ret XZ_FUNC dec_block_header(struct xz_dec *s) +{ + enum xz_ret ret; + + /* + * Validate the CRC32. We know that the temp buffer is at least + * eight bytes so this is safe. + */ + s->temp.size -= 4; + if (xz_crc32(s->temp.buf, s->temp.size, 0) + != get_le32(s->temp.buf + s->temp.size)) + return XZ_DATA_ERROR; + + s->temp.pos = 2; + + /* + * Catch unsupported Block Flags. We support only one or two filters + * in the chain, so we catch that with the same test. + */ +#ifdef XZ_DEC_BCJ + if (s->temp.buf[1] & 0x3E) +#else + if (s->temp.buf[1] & 0x3F) +#endif + return XZ_OPTIONS_ERROR; + + /* Compressed Size */ + if (s->temp.buf[1] & 0x40) { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) + != XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.compressed = s->vli; + } else { + s->block_header.compressed = VLI_UNKNOWN; + } + + /* Uncompressed Size */ + if (s->temp.buf[1] & 0x80) { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) + != XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.uncompressed = s->vli; + } else { + s->block_header.uncompressed = VLI_UNKNOWN; + } + +#ifdef XZ_DEC_BCJ + /* If there are two filters, the first one must be a BCJ filter. */ + s->bcj_active = s->temp.buf[1] & 0x01; + if (s->bcj_active) { + if (s->temp.size - s->temp.pos < 2) + return XZ_OPTIONS_ERROR; + + ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]); + if (ret != XZ_OK) + return ret; + + /* + * We don't support custom start offset, + * so Size of Properties must be zero. + */ + if (s->temp.buf[s->temp.pos++] != 0x00) + return XZ_OPTIONS_ERROR; + } +#endif + + /* Valid Filter Flags always take at least two bytes. */ + if (s->temp.size - s->temp.pos < 2) + return XZ_DATA_ERROR; + + /* Filter ID = LZMA2 */ + if (s->temp.buf[s->temp.pos++] != 0x21) + return XZ_OPTIONS_ERROR; + + /* Size of Properties = 1-byte Filter Properties */ + if (s->temp.buf[s->temp.pos++] != 0x01) + return XZ_OPTIONS_ERROR; + + /* Filter Properties contains LZMA2 dictionary size. */ + if (s->temp.size - s->temp.pos < 1) + return XZ_DATA_ERROR; + + ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]); + if (ret != XZ_OK) + return ret; + + /* The rest must be Header Padding. */ + while (s->temp.pos < s->temp.size) + if (s->temp.buf[s->temp.pos++] != 0x00) + return XZ_OPTIONS_ERROR; + + s->temp.pos = 0; + s->block.compressed = 0; + s->block.uncompressed = 0; + + return XZ_OK; +} + +static enum xz_ret XZ_FUNC dec_main(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + /* + * Store the start position for the case when we are in the middle + * of the Index field. + */ + s->in_start = b->in_pos; + + while (true) { + switch (s->sequence) { + case SEQ_STREAM_HEADER: + /* + * Stream Header is copied to s->temp, and then + * decoded from there. This way if the caller + * gives us only little input at a time, we can + * still keep the Stream Header decoding code + * simple. Similar approach is used in many places + * in this file. + */ + if (!fill_temp(s, b)) + return XZ_OK; + + /* + * If dec_stream_header() returns + * XZ_UNSUPPORTED_CHECK, it is still possible + * to continue decoding if working in multi-call + * mode. Thus, update s->sequence before calling + * dec_stream_header(). + */ + s->sequence = SEQ_BLOCK_START; + + ret = dec_stream_header(s); + if (ret != XZ_OK) + return ret; + + case SEQ_BLOCK_START: + /* We need one byte of input to continue. */ + if (b->in_pos == b->in_size) + return XZ_OK; + + /* See if this is the beginning of the Index field. */ + if (b->in[b->in_pos] == 0) { + s->in_start = b->in_pos++; + s->sequence = SEQ_INDEX; + break; + } + + /* + * Calculate the size of the Block Header and + * prepare to decode it. + */ + s->block_header.size + = ((uint32_t)b->in[b->in_pos] + 1) * 4; + + s->temp.size = s->block_header.size; + s->temp.pos = 0; + s->sequence = SEQ_BLOCK_HEADER; + + case SEQ_BLOCK_HEADER: + if (!fill_temp(s, b)) + return XZ_OK; + + ret = dec_block_header(s); + if (ret != XZ_OK) + return ret; + + s->sequence = SEQ_BLOCK_UNCOMPRESS; + + case SEQ_BLOCK_UNCOMPRESS: + ret = dec_block(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->sequence = SEQ_BLOCK_PADDING; + + case SEQ_BLOCK_PADDING: + /* + * Size of Compressed Data + Block Padding + * must be a multiple of four. We don't need + * s->block.compressed for anything else + * anymore, so we use it here to test the size + * of the Block Padding field. + */ + while (s->block.compressed & 3) { + if (b->in_pos == b->in_size) + return XZ_OK; + + if (b->in[b->in_pos++] != 0) + return XZ_DATA_ERROR; + + ++s->block.compressed; + } + + s->sequence = SEQ_BLOCK_CHECK; + + case SEQ_BLOCK_CHECK: + if (s->check_type == XZ_CHECK_CRC32) { + ret = crc32_validate(s, b); + if (ret != XZ_STREAM_END) + return ret; + } +#ifdef XZ_DEC_ANY_CHECK + else if (!check_skip(s, b)) { + return XZ_OK; + } +#endif + + s->sequence = SEQ_BLOCK_START; + break; + + case SEQ_INDEX: + ret = dec_index(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->sequence = SEQ_INDEX_PADDING; + + case SEQ_INDEX_PADDING: + while ((s->index.size + (b->in_pos - s->in_start)) + & 3) { + if (b->in_pos == b->in_size) { + index_update(s, b); + return XZ_OK; + } + + if (b->in[b->in_pos++] != 0) + return XZ_DATA_ERROR; + } + + /* Finish the CRC32 value and Index size. */ + index_update(s, b); + + /* Compare the hashes to validate the Index field. */ + if (!memeq(&s->block.hash, &s->index.hash, + sizeof(s->block.hash))) + return XZ_DATA_ERROR; + + s->sequence = SEQ_INDEX_CRC32; + + case SEQ_INDEX_CRC32: + ret = crc32_validate(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->temp.size = STREAM_HEADER_SIZE; + s->sequence = SEQ_STREAM_FOOTER; + + case SEQ_STREAM_FOOTER: + if (!fill_temp(s, b)) + return XZ_OK; + + return dec_stream_footer(s); + } + } + + /* Never reached */ +} + +/* + * xz_dec_run() is a wrapper for dec_main() to handle some special cases in + * multi-call and single-call decoding. + * + * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we + * are not going to make any progress anymore. This is to prevent the caller + * from calling us infinitely when the input file is truncated or otherwise + * corrupt. Since zlib-style API allows that the caller fills the input buffer + * only when the decoder doesn't produce any new output, we have to be careful + * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only + * after the second consecutive call to xz_dec_run() that makes no progress. + * + * In single-call mode, if we couldn't decode everything and no error + * occurred, either the input is truncated or the output buffer is too small. + * Since we know that the last input byte never produces any output, we know + * that if all the input was consumed and decoding wasn't finished, the file + * must be corrupt. Otherwise the output buffer has to be too small or the + * file is corrupt in a way that decoding it produces too big output. + * + * If single-call decoding fails, we reset b->in_pos and b->out_pos back to + * their original values. This is because with some filter chains there won't + * be any valid uncompressed data in the output buffer unless the decoding + * actually succeeds (that's the price to pay of using the output buffer as + * the workspace). + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_run(struct xz_dec *s, struct xz_buf *b) +{ + size_t in_start; + size_t out_start; + enum xz_ret ret; + + if (DEC_IS_SINGLE(s->mode)) + xz_dec_reset(s); + + in_start = b->in_pos; + out_start = b->out_pos; + ret = dec_main(s, b); + + if (DEC_IS_SINGLE(s->mode)) { + if (ret == XZ_OK) + ret = b->in_pos == b->in_size + ? XZ_DATA_ERROR : XZ_BUF_ERROR; + + if (ret != XZ_STREAM_END) { + 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) + ret = XZ_BUF_ERROR; + + s->allow_buf_error = true; + } else { + s->allow_buf_error = false; + } + + return ret; +} + +XZ_EXTERN struct xz_dec * XZ_FUNC xz_dec_init( + enum xz_mode mode, uint32_t dict_max) +{ + struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->mode = mode; + +#ifdef XZ_DEC_BCJ + s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode)); + if (s->bcj == NULL) + goto error_bcj; +#endif + + s->lzma2 = xz_dec_lzma2_create(mode, dict_max); + if (s->lzma2 == NULL) + goto error_lzma2; + + xz_dec_reset(s); + return s; + +error_lzma2: +#ifdef XZ_DEC_BCJ + xz_dec_bcj_end(s->bcj); +error_bcj: +#endif + kfree(s); + return NULL; +} + +XZ_EXTERN void XZ_FUNC xz_dec_reset(struct xz_dec *s) +{ + s->sequence = SEQ_STREAM_HEADER; + s->allow_buf_error = false; + s->pos = 0; + s->crc32 = 0; + memzero(&s->block, sizeof(s->block)); + memzero(&s->index, sizeof(s->index)); + s->temp.pos = 0; + s->temp.size = STREAM_HEADER_SIZE; +} + +XZ_EXTERN void XZ_FUNC xz_dec_end(struct xz_dec *s) +{ + if (s != NULL) { + xz_dec_lzma2_end(s->lzma2); +#ifdef XZ_DEC_BCJ + xz_dec_bcj_end(s->bcj); +#endif + kfree(s); + } +} diff --git a/busybox/archival/libarchive/unxz/xz_lzma2.h b/busybox/archival/libarchive/unxz/xz_lzma2.h new file mode 100644 index 00000000000000..47f21afbc96aa1 --- /dev/null +++ b/busybox/archival/libarchive/unxz/xz_lzma2.h @@ -0,0 +1,204 @@ +/* + * LZMA2 definitions + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_LZMA2_H +#define XZ_LZMA2_H + +/* Range coder constants */ +#define RC_SHIFT_BITS 8 +#define RC_TOP_BITS 24 +#define RC_TOP_VALUE (1 << RC_TOP_BITS) +#define RC_BIT_MODEL_TOTAL_BITS 11 +#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS) +#define RC_MOVE_BITS 5 + +/* + * Maximum number of position states. A position state is the lowest pb + * number of bits of the current uncompressed offset. In some places there + * are different sets of probabilities for different position states. + */ +#define POS_STATES_MAX (1 << 4) + +/* + * This enum is used to track which LZMA symbols have occurred most recently + * and in which order. This information is used to predict the next symbol. + * + * Symbols: + * - Literal: One 8-bit byte + * - Match: Repeat a chunk of data at some distance + * - Long repeat: Multi-byte match at a recently seen distance + * - Short repeat: One-byte repeat at a recently seen distance + * + * The symbol names are in from STATE_oldest_older_previous. REP means + * either short or long repeated match, and NONLIT means any non-literal. + */ +enum lzma_state { + STATE_LIT_LIT, + STATE_MATCH_LIT_LIT, + STATE_REP_LIT_LIT, + STATE_SHORTREP_LIT_LIT, + STATE_MATCH_LIT, + STATE_REP_LIT, + STATE_SHORTREP_LIT, + STATE_LIT_MATCH, + STATE_LIT_LONGREP, + STATE_LIT_SHORTREP, + STATE_NONLIT_MATCH, + STATE_NONLIT_REP +}; + +/* Total number of states */ +#define STATES 12 + +/* The lowest 7 states indicate that the previous state was a literal. */ +#define LIT_STATES 7 + +/* Indicate that the latest symbol was a literal. */ +static inline void XZ_FUNC lzma_state_literal(enum lzma_state *state) +{ + if (*state <= STATE_SHORTREP_LIT_LIT) + *state = STATE_LIT_LIT; + else if (*state <= STATE_LIT_SHORTREP) + *state -= 3; + else + *state -= 6; +} + +/* Indicate that the latest symbol was a match. */ +static inline void XZ_FUNC lzma_state_match(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH; +} + +/* Indicate that the latest state was a long repeated match. */ +static inline void XZ_FUNC lzma_state_long_rep(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP; +} + +/* Indicate that the latest symbol was a short match. */ +static inline void XZ_FUNC lzma_state_short_rep(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP; +} + +/* Test if the previous symbol was a literal. */ +static inline bool XZ_FUNC lzma_state_is_literal(enum lzma_state state) +{ + return state < LIT_STATES; +} + +/* Each literal coder is divided in three sections: + * - 0x001-0x0FF: Without match byte + * - 0x101-0x1FF: With match byte; match bit is 0 + * - 0x201-0x2FF: With match byte; match bit is 1 + * + * Match byte is used when the previous LZMA symbol was something else than + * a literal (that is, it was some kind of match). + */ +#define LITERAL_CODER_SIZE 0x300 + +/* Maximum number of literal coders */ +#define LITERAL_CODERS_MAX (1 << 4) + +/* Minimum length of a match is two bytes. */ +#define MATCH_LEN_MIN 2 + +/* Match length is encoded with 4, 5, or 10 bits. + * + * Length Bits + * 2-9 4 = Choice=0 + 3 bits + * 10-17 5 = Choice=1 + Choice2=0 + 3 bits + * 18-273 10 = Choice=1 + Choice2=1 + 8 bits + */ +#define LEN_LOW_BITS 3 +#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS) +#define LEN_MID_BITS 3 +#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS) +#define LEN_HIGH_BITS 8 +#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS) +#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS) + +/* + * Maximum length of a match is 273 which is a result of the encoding + * described above. + */ +#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1) + +/* + * Different sets of probabilities are used for match distances that have + * very short match length: Lengths of 2, 3, and 4 bytes have a separate + * set of probabilities for each length. The matches with longer length + * use a shared set of probabilities. + */ +#define DIST_STATES 4 + +/* + * Get the index of the appropriate probability array for decoding + * the distance slot. + */ +static inline uint32_t XZ_FUNC lzma_get_dist_state(uint32_t len) +{ + return len < DIST_STATES + MATCH_LEN_MIN + ? len - MATCH_LEN_MIN : DIST_STATES - 1; +} + +/* + * The highest two bits of a 32-bit match distance are encoded using six bits. + * This six-bit value is called a distance slot. This way encoding a 32-bit + * value takes 6-36 bits, larger values taking more bits. + */ +#define DIST_SLOT_BITS 6 +#define DIST_SLOTS (1 << DIST_SLOT_BITS) + +/* Match distances up to 127 are fully encoded using probabilities. Since + * the highest two bits (distance slot) are always encoded using six bits, + * the distances 0-3 don't need any additional bits to encode, since the + * distance slot itself is the same as the actual distance. DIST_MODEL_START + * indicates the first distance slot where at least one additional bit is + * needed. + */ +#define DIST_MODEL_START 4 + +/* + * Match distances greater than 127 are encoded in three pieces: + * - distance slot: the highest two bits + * - direct bits: 2-26 bits below the highest two bits + * - alignment bits: four lowest bits + * + * Direct bits don't use any probabilities. + * + * The distance slot value of 14 is for distances 128-191. + */ +#define DIST_MODEL_END 14 + +/* Distance slots that indicate a distance <= 127. */ +#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2) +#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS) + +/* + * For match distances greater than 127, only the highest two bits and the + * lowest four bits (alignment) is encoded using probabilities. + */ +#define ALIGN_BITS 4 +#define ALIGN_SIZE (1 << ALIGN_BITS) +#define ALIGN_MASK (ALIGN_SIZE - 1) + +/* Total number of all probability variables */ +#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE) + +/* + * LZMA remembers the four most recent match distances. Reusing these + * distances tends to take less space than re-encoding the actual + * distance value. + */ +#define REPS 4 + +#endif diff --git a/busybox/archival/libarchive/unxz/xz_private.h b/busybox/archival/libarchive/unxz/xz_private.h new file mode 100644 index 00000000000000..145649a83c7b68 --- /dev/null +++ b/busybox/archival/libarchive/unxz/xz_private.h @@ -0,0 +1,159 @@ +/* + * Private includes and definitions + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_PRIVATE_H +#define XZ_PRIVATE_H + +#ifdef __KERNEL__ + /* XZ_PREBOOT may be defined only via decompress_unxz.c. */ +# ifndef XZ_PREBOOT +# include +# include +# include +# define memeq(a, b, size) (memcmp(a, b, size) == 0) +# define memzero(buf, size) memset(buf, 0, size) +# endif +# include +# include +# define get_le32(p) le32_to_cpup((const uint32_t *)(p)) + /* XZ_IGNORE_KCONFIG may be defined only via decompress_unxz.c. */ +# ifndef XZ_IGNORE_KCONFIG +# ifdef CONFIG_XZ_DEC_X86 +# define XZ_DEC_X86 +# endif +# ifdef CONFIG_XZ_DEC_POWERPC +# define XZ_DEC_POWERPC +# endif +# ifdef CONFIG_XZ_DEC_IA64 +# define XZ_DEC_IA64 +# endif +# ifdef CONFIG_XZ_DEC_ARM +# define XZ_DEC_ARM +# endif +# ifdef CONFIG_XZ_DEC_ARMTHUMB +# define XZ_DEC_ARMTHUMB +# endif +# ifdef CONFIG_XZ_DEC_SPARC +# define XZ_DEC_SPARC +# endif +# endif +# include +#else + /* + * For userspace builds, use a separate header to define the required + * macros and functions. This makes it easier to adapt the code into + * different environments and avoids clutter in the Linux kernel tree. + */ +# include "xz_config.h" +#endif + +/* If no specific decoding mode is requested, enable support for all modes. */ +#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \ + && !defined(XZ_DEC_DYNALLOC) +# define XZ_DEC_SINGLE +# define XZ_DEC_PREALLOC +# define XZ_DEC_DYNALLOC +#endif + +/* + * The DEC_IS_foo(mode) macros are used in "if" statements. If only some + * of the supported modes are enabled, these macros will evaluate to true or + * false at compile time and thus allow the compiler to omit unneeded code. + */ +#ifdef XZ_DEC_SINGLE +# define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE) +#else +# define DEC_IS_SINGLE(mode) (false) +#endif + +#ifdef XZ_DEC_PREALLOC +# define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC) +#else +# define DEC_IS_PREALLOC(mode) (false) +#endif + +#ifdef XZ_DEC_DYNALLOC +# define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC) +#else +# define DEC_IS_DYNALLOC(mode) (false) +#endif + +#if !defined(XZ_DEC_SINGLE) +# define DEC_IS_MULTI(mode) (true) +#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC) +# define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE) +#else +# define DEC_IS_MULTI(mode) (false) +#endif + +/* + * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ. + * XZ_DEC_BCJ is used to enable generic support for BCJ decoders. + */ +#ifndef XZ_DEC_BCJ +# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \ + || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \ + || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \ + || defined(XZ_DEC_SPARC) +# define XZ_DEC_BCJ +# endif +#endif + +/* + * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used + * before calling xz_dec_lzma2_run(). + */ +XZ_EXTERN struct xz_dec_lzma2 * XZ_FUNC xz_dec_lzma2_create( + enum xz_mode mode, uint32_t dict_max); + +/* + * Decode the LZMA2 properties (one byte) and reset the decoder. Return + * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not + * big enough, and XZ_OPTIONS_ERROR if props indicates something that this + * decoder doesn't support. + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_lzma2_reset( + struct xz_dec_lzma2 *s, uint8_t props); + +/* Decode raw LZMA2 stream from b->in to b->out. */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_lzma2_run( + struct xz_dec_lzma2 *s, struct xz_buf *b); + +/* Free the memory allocated for the LZMA2 decoder. */ +XZ_EXTERN void XZ_FUNC xz_dec_lzma2_end(struct xz_dec_lzma2 *s); + +#ifdef XZ_DEC_BCJ +/* + * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before + * calling xz_dec_bcj_run(). + */ +XZ_EXTERN struct xz_dec_bcj * XZ_FUNC xz_dec_bcj_create(bool single_call); + +/* + * Decode the Filter ID of a BCJ filter. This implementation doesn't + * support custom start offsets, so no decoding of Filter Properties + * is needed. Returns XZ_OK if the given Filter ID is supported. + * Otherwise XZ_OPTIONS_ERROR is returned. + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_reset( + struct xz_dec_bcj *s, uint8_t id); + +/* + * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is + * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run() + * must be called directly. + */ +XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_run(struct xz_dec_bcj *s, + struct xz_dec_lzma2 *lzma2, struct xz_buf *b); + +/* Free the memory allocated for the BCJ filters. */ +#define xz_dec_bcj_end(s) kfree(s) +#endif + +#endif diff --git a/busybox/archival/libarchive/unxz/xz_stream.h b/busybox/archival/libarchive/unxz/xz_stream.h new file mode 100644 index 00000000000000..66cb5a7055ec83 --- /dev/null +++ b/busybox/archival/libarchive/unxz/xz_stream.h @@ -0,0 +1,62 @@ +/* + * Definitions for handling the .xz file format + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_STREAM_H +#define XZ_STREAM_H + +#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32 +# include +# undef crc32 +# define xz_crc32(buf, size, crc) \ + (~crc32_le(~(uint32_t)(crc), buf, size)) +#endif + +/* + * See the .xz file format specification at + * http://tukaani.org/xz/xz-file-format.txt + * to understand the container format. + */ + +#define STREAM_HEADER_SIZE 12 + +#define HEADER_MAGIC "\3757zXZ" +#define HEADER_MAGIC_SIZE 6 + +#define FOOTER_MAGIC "YZ" +#define FOOTER_MAGIC_SIZE 2 + +/* + * Variable-length integer can hold a 63-bit unsigned integer or a special + * value indicating that the value is unknown. + * + * Experimental: vli_type can be defined to uint32_t to save a few bytes + * in code size (no effect on speed). Doing so limits the uncompressed and + * compressed size of the file to less than 256 MiB and may also weaken + * error detection slightly. + */ +typedef uint64_t vli_type; + +#define VLI_MAX ((vli_type)-1 / 2) +#define VLI_UNKNOWN ((vli_type)-1) + +/* Maximum encoded size of a VLI */ +#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7) + +/* Integrity Check types */ +enum xz_check { + XZ_CHECK_NONE = 0, + XZ_CHECK_CRC32 = 1, + XZ_CHECK_CRC64 = 4, + XZ_CHECK_SHA256 = 10 +}; + +/* Maximum possible Check ID */ +#define XZ_CHECK_MAX 15 + +#endif diff --git a/busybox/archival/lzop.c b/busybox/archival/lzop.c new file mode 100644 index 00000000000000..8f604254c7f1e6 --- /dev/null +++ b/busybox/archival/lzop.c @@ -0,0 +1,1144 @@ +/* + This file is part of the lzop file compressor. + + Copyright (C) 1996..2003 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + Markus F.X.J. Oberhumer + http://www.oberhumer.com/opensource/lzop/ + + lzop and the LZO library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + "Minimalized" for busybox by Alain Knaff +*/ +//config:config LZOP +//config: bool "lzop (13 kb)" +//config: default y +//config: help +//config: Lzop compression/decompresion. +//config: +//config:config UNLZOP +//config: bool "unlzop (13 kb)" +//config: default n # INCOMPAT: upstream lzop does not provide such tool +//config: help +//config: Lzop decompresion. +//config: +//config:config LZOPCAT +//config: bool "lzopcat (13 kb)" +//config: default n # INCOMPAT: upstream lzop does not provide such tool +//config: help +//config: Alias to "lzop -dc". +//config: +//config:config LZOP_COMPR_HIGH +//config: bool "lzop compression levels 7,8,9 (not very useful)" +//config: default n +//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_ODDNAME:name main location suid_type help +//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: "[-cfUvd123456789CF] [FILE]..." +//usage:#define lzop_full_usage "\n\n" +//usage: " -1..9 Compression level" +//usage: "\n -d Decompress" +//usage: "\n -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -U Delete input files" +///////: "\n -k Keep input files" (default, so why bother documenting?) +//usage: "\n -v Verbose" +//usage: "\n -F Don't store or verify checksum" +//usage: "\n -C Also write checksum of compressed block" +//usage: +//usage:#define lzopcat_trivial_usage +//usage: "[-vF] [FILE]..." +//usage:#define lzopcat_full_usage "\n\n" +//usage: " -v Verbose" +//usage: "\n -F Don't verify checksum" +//usage: +//usage:#define unlzop_trivial_usage +//usage: "[-cfUvF] [FILE]..." +//usage:#define unlzop_full_usage "\n\n" +//usage: " -c Write to stdout" +//usage: "\n -f Force" +//usage: "\n -U Delete input files" +///////: "\n -k Keep input files" (default, so why bother documenting?) +//usage: "\n -v Verbose" +//usage: "\n -F Don't verify checksum" + +#include "libbb.h" +#include "common_bufsiz.h" +#include "bb_archive.h" +#include "liblzo_interface.h" + +/* lzo-2.03/src/lzo_ptr.h */ +#define pd(a,b) ((unsigned)((a)-(b))) + +#define lzo_version() LZO_VERSION +#define lzo_sizeof_dict_t (sizeof(uint8_t*)) + +/* lzo-2.03/include/lzo/lzo1x.h */ +#define LZO1X_1_MEM_COMPRESS (16384 * lzo_sizeof_dict_t) +#define LZO1X_1_15_MEM_COMPRESS (32768 * lzo_sizeof_dict_t) +#define LZO1X_999_MEM_COMPRESS (14 * 16384 * sizeof(short)) + +/* lzo-2.03/src/lzo1x_oo.c */ +#define NO_LIT UINT_MAX + +/**********************************************************************/ +static void copy2(uint8_t* ip, const uint8_t* m_pos, unsigned off) +{ + ip[0] = m_pos[0]; + if (off == 1) + ip[1] = m_pos[0]; + else + ip[1] = m_pos[1]; +} + +static void copy3(uint8_t* ip, const uint8_t* m_pos, unsigned off) +{ + ip[0] = m_pos[0]; + if (off == 1) { + ip[2] = ip[1] = m_pos[0]; + } + else if (off == 2) { + ip[1] = m_pos[1]; + ip[2] = m_pos[0]; + } + else { + ip[1] = m_pos[1]; + ip[2] = m_pos[2]; + } +} + +/**********************************************************************/ +// optimize a block of data. +/**********************************************************************/ +#define TEST_IP (ip < ip_end) +#define TEST_OP (op <= op_end) + +static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len, + uint8_t *out, unsigned *out_len /*, void* wrkmem */) +{ + uint8_t* op; + uint8_t* ip; + unsigned t; + uint8_t* m_pos; + uint8_t* const ip_end = in + in_len; + uint8_t* const op_end = out + *out_len; + uint8_t* litp = NULL; + unsigned lit = 0; + unsigned next_lit = NO_LIT; + unsigned nl; + unsigned long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0; + +// LZO_UNUSED(wrkmem); + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) { + t = *ip++ - 17; + if (t < 4) + goto match_next; + goto first_literal_run; + } + + while (TEST_IP && TEST_OP) { + t = *ip++; + if (t >= 16) + goto match; + /* a literal run */ + litp = ip - 1; + if (t == 0) { + t = 15; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + lit = t + 3; + /* copy literals */ + copy_literal_run: + *op++ = *ip++; + *op++ = *ip++; + *op++ = *ip++; + first_literal_run: + do *op++ = *ip++; while (--t > 0); + + t = *ip++; + + if (t >= 16) + goto match; +#if defined(LZO1X) + m_pos = op - 1 - 0x800; +#elif defined(LZO1Y) + m_pos = op - 1 - 0x400; +#endif + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos++; + lit = 0; + goto match_done; + + + /* handle matches */ + do { + if (t < 16) { /* a M1 match */ + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + + if (litp == NULL) + goto copy_m1; + + nl = ip[-2] & 3; + /* test if a match follows */ + if (nl == 0 && lit == 1 && ip[0] >= 16) { + next_lit = nl; + /* adjust length of previous short run */ + lit += 2; + *litp = (unsigned char)((*litp & ~3) | lit); + /* copy over the 2 literals that replace the match */ + copy2(ip-2, m_pos, pd(op, m_pos)); + o_m1_a++; + } + /* test if a literal run follows */ + else + if (nl == 0 + && ip[0] < 16 + && ip[0] != 0 + && (lit + 2 + ip[0] < 16) + ) { + t = *ip++; + /* remove short run */ + *litp &= ~3; + /* copy over the 2 literals that replace the match */ + copy2(ip-3+1, m_pos, pd(op, m_pos)); + /* move literals 1 byte ahead */ + litp += 2; + if (lit > 0) + memmove(litp+1, litp, lit); + /* insert new length of long literal run */ + lit += 2 + t + 3; + *litp = (unsigned char)(lit - 3); + + o_m1_b++; + *op++ = *m_pos++; + *op++ = *m_pos++; + goto copy_literal_run; + } + copy_m1: + *op++ = *m_pos++; + *op++ = *m_pos++; + } else { + match: + if (t >= 64) { /* a M2 match */ + m_pos = op - 1; +#if defined(LZO1X) + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#endif + if (litp == NULL) + goto copy_m; + + nl = ip[-2] & 3; + /* test if in beetween two long literal runs */ + if (t == 1 && lit > 3 && nl == 0 + && ip[0] < 16 && ip[0] != 0 && (lit + 3 + ip[0] < 16) + ) { + t = *ip++; + /* copy over the 3 literals that replace the match */ + copy3(ip-1-2, m_pos, pd(op, m_pos)); + /* set new length of previous literal run */ + lit += 3 + t + 3; + *litp = (unsigned char)(lit - 3); + o_m2++; + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos++; + goto copy_literal_run; + } + } else { + if (t >= 32) { /* a M3 match */ + t &= 31; + if (t == 0) { + t = 31; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + m_pos = op - 1; + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + } else { /* a M4 match */ + m_pos = op; + m_pos -= (t & 8) << 11; + t &= 7; + if (t == 0) { + t = 7; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; + } + if (litp == NULL) + goto copy_m; + + nl = ip[-2] & 3; + /* test if in beetween two matches */ + if (t == 1 && lit == 0 && nl == 0 && ip[0] >= 16) { + next_lit = nl; + /* make a previous short run */ + lit += 3; + *litp = (unsigned char)((*litp & ~3) | lit); + /* copy over the 3 literals that replace the match */ + copy3(ip-3, m_pos, pd(op, m_pos)); + o_m3_a++; + } + /* test if a literal run follows */ + else if (t == 1 && lit <= 3 && nl == 0 + && ip[0] < 16 && ip[0] != 0 && (lit + 3 + ip[0] < 16) + ) { + t = *ip++; + /* remove short run */ + *litp &= ~3; + /* copy over the 3 literals that replace the match */ + copy3(ip-4+1, m_pos, pd(op, m_pos)); + /* move literals 1 byte ahead */ + litp += 2; + if (lit > 0) + memmove(litp+1,litp,lit); + /* insert new length of long literal run */ + lit += 3 + t + 3; + *litp = (unsigned char)(lit - 3); + + o_m3_b++; + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos++; + goto copy_literal_run; + } + } + copy_m: + *op++ = *m_pos++; + *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + + match_done: + if (next_lit == NO_LIT) { + t = ip[-2] & 3; + lit = t; + litp = ip - 2; + } + else + t = next_lit; + next_lit = NO_LIT; + if (t == 0) + break; + /* copy literals */ + match_next: + do *op++ = *ip++; while (--t > 0); + t = *ip++; + } while (TEST_IP && TEST_OP); + } + + /* no EOF code was found */ + *out_len = pd(op, out); + return LZO_E_EOF_NOT_FOUND; + + eof_found: +// LZO_UNUSED(o_m1_a); LZO_UNUSED(o_m1_b); LZO_UNUSED(o_m2); +// LZO_UNUSED(o_m3_a); LZO_UNUSED(o_m3_b); + *out_len = pd(op, out); + return (ip == ip_end ? LZO_E_OK : + (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); +} + +/**********************************************************************/ +#define F_OS F_OS_UNIX +#define F_CS F_CS_NATIVE + +/**********************************************************************/ +#define ADLER32_INIT_VALUE 1 +#define CRC32_INIT_VALUE 0 + +/**********************************************************************/ +enum { + M_LZO1X_1 = 1, + M_LZO1X_1_15 = 2, + M_LZO1X_999 = 3, +}; + +/**********************************************************************/ +/* header flags */ +#define F_ADLER32_D 0x00000001L +#define F_ADLER32_C 0x00000002L +#define F_H_EXTRA_FIELD 0x00000040L +#define F_H_GMTDIFF 0x00000080L +#define F_CRC32_D 0x00000100L +#define F_CRC32_C 0x00000200L +#define F_H_FILTER 0x00000800L +#define F_H_CRC32 0x00001000L +#define F_MASK 0x00003FFFL + +/* operating system & file system that created the file [mostly unused] */ +#define F_OS_UNIX 0x03000000L +#define F_OS_SHIFT 24 +#define F_OS_MASK 0xff000000L + +/* character set for file name encoding [mostly unused] */ +#define F_CS_NATIVE 0x00000000L +#define F_CS_SHIFT 20 +#define F_CS_MASK 0x00f00000L + +/* these bits must be zero */ +#define F_RESERVED ((F_MASK | F_OS_MASK | F_CS_MASK) ^ 0xffffffffL) + +typedef struct chksum_t { + uint32_t f_adler32; + uint32_t f_crc32; +} chksum_t; + +typedef struct header_t { + /* used to have auxiliary fields here */ + + /* Starting from here, the layout and endianness + * are exactly in on-disk format. + */ + uint16_t version_be16; + uint16_t lib_version_be16; + uint16_t version_needed_to_extract_be16; + uint8_t method; + uint8_t level; + uint32_t flags32; /* be32 on disk, but we keep this field in native order */ + uint32_t mode_be32; + uint32_t mtime_be32; + uint32_t gmtdiff_be32; + char len_and_name[1+255+1]; +} header_t; + +struct globals { + /*const uint32_t *lzo_crc32_table;*/ + chksum_t chksum; +} FIX_ALIASING; +#define G (*(struct globals*)bb_common_bufsiz1) +//#define G (*ptr_to_globals) +#define INIT_G() do { \ + setup_common_bufsiz(); \ + /*SET_PTR_TO_GLOBALS(xzalloc(sizeof(G)));*/ \ +} while (0) + + +/**********************************************************************/ +#define LZOP_VERSION 0x1010 +//#define LZOP_VERSION_STRING "1.01" +//#define LZOP_VERSION_DATE "Apr 27th 2003" + +// lzop wants to be weird: +// unlike all other compressosrs, its -k "keep" option is the default, +// and -U is used to delete the source. We will invert the bit after getopt(). +#define OPTION_STRING "cfUvqdt123456789CFk" + +/* Note: must be kept in sync with archival/bbunzip.c */ +enum { + OPT_STDOUT = (1 << 0), + OPT_FORCE = (1 << 1), + OPT_KEEP = (1 << 2), + OPT_VERBOSE = (1 << 3), + OPT_QUIET = (1 << 4), + OPT_DECOMPRESS = (1 << 5), + OPT_TEST = (1 << 6), + OPT_1 = (1 << 7), + OPT_2 = (1 << 8), + OPT_3 = (1 << 9), + OPT_4 = (1 << 10), + OPT_5 = (1 << 11), + OPT_6 = (1 << 12), + OPT_7 = (1 << 13), + OPT_8 = (1 << 14), + OPT_9 = (1 << 15), + OPT_C = (1 << 16), + OPT_F = (1 << 17), + OPT_k = (1 << 18), + OPT_789 = OPT_7 | OPT_8 | OPT_9 +}; + +/**********************************************************************/ +// adler32 checksum +// adapted from free code by Mark Adler +// see http://www.zlib.org/ +/**********************************************************************/ +static FAST_FUNC uint32_t +lzo_adler32(uint32_t adler, const uint8_t* buf, unsigned len) +{ + enum { + LZO_BASE = 65521, /* largest prime smaller than 65536 */ + /* NMAX is the largest n such that + * 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + LZO_NMAX = 5552, + }; + uint32_t s1 = adler & 0xffff; + uint32_t s2 = (adler >> 16) & 0xffff; + unsigned k; + + if (buf == NULL) + return 1; + + while (len > 0) { + k = len < LZO_NMAX ? (unsigned) len : LZO_NMAX; + len -= k; + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k > 0); + s1 %= LZO_BASE; + s2 %= LZO_BASE; + } + return (s2 << 16) | s1; +} + +static FAST_FUNC uint32_t +lzo_crc32(uint32_t c, const uint8_t* buf, unsigned len) +{ + //if (buf == NULL) - impossible + // return 0; + + return ~crc32_block_endian0(~c, buf, len, global_crc32_table); +} + +/**********************************************************************/ +static void init_chksum(void) +{ + G.chksum.f_adler32 = ADLER32_INIT_VALUE; + G.chksum.f_crc32 = CRC32_INIT_VALUE; +} + +static void add_bytes_to_chksum(const void* buf, int cnt) +{ + /* We need to handle the two checksums at once, because at the + * beginning of the header, we don't know yet which one we'll + * eventually need */ + G.chksum.f_adler32 = lzo_adler32(G.chksum.f_adler32, (const uint8_t*)buf, cnt); + G.chksum.f_crc32 = lzo_crc32(G.chksum.f_crc32, (const uint8_t*)buf, cnt); +} + +static uint32_t chksum_getresult(uint32_t h_flags32) +{ + return (h_flags32 & F_H_CRC32) ? G.chksum.f_crc32 : G.chksum.f_adler32; +} + +/**********************************************************************/ +static uint32_t read32(void) +{ + uint32_t v; + xread(0, &v, 4); + return ntohl(v); +} +static void f_read(void* buf, int cnt) +{ + xread(0, buf, cnt); + add_bytes_to_chksum(buf, cnt); +} +//static int f_read8(void) +//{ +// uint8_t v; +// f_read(&v, 1); +// return v; +//} +//static unsigned f_read16(void) +//{ +// uint16_t v; +// f_read(&v, 2); +// return ntohs(v); +//} +static uint32_t f_read32(void) +{ + uint32_t v; + f_read(&v, 4); + return ntohl(v); +} + +static void write32(uint32_t v) +{ + v = htonl(v); + xwrite(1, &v, 4); +} +static void f_write(const void* buf, int cnt) +{ + xwrite(1, buf, cnt); + add_bytes_to_chksum(buf, cnt); +} +//static void f_write8(uint8_t v) +//{ +// f_write(&v, 1); +//} +//static void f_write16(uint16_t v) +//{ +// v = htons(v); +// f_write(&v, 2); +//} +//static void f_write32(uint32_t v) +//{ +// v = htonl(v); +// f_write(&v, 4); +//} + +/**********************************************************************/ +#define LZO_BLOCK_SIZE (256 * 1024l) +#define MAX_BLOCK_SIZE (64 * 1024l * 1024l) /* DO NOT CHANGE */ + +/* LZO may expand uncompressible data by a small amount */ +#define MAX_COMPRESSED_SIZE(x) ((x) + (x) / 16 + 64 + 3) + +/**********************************************************************/ +// compress a file +/**********************************************************************/ +static NOINLINE int lzo_compress(const header_t *h) +{ + unsigned block_size = LZO_BLOCK_SIZE; + int r = 0; /* LZO_E_OK */ + uint8_t *const b1 = xzalloc(block_size); + uint8_t *const b2 = xzalloc(MAX_COMPRESSED_SIZE(block_size)); + uint32_t d_adler32 = ADLER32_INIT_VALUE; + uint32_t d_crc32 = CRC32_INIT_VALUE; + uint8_t *wrk_mem = NULL; + + /* Only these methods are possible, see lzo_set_method(): + * -1: M_LZO1X_1_15 + * -2..6: M_LZO1X_1 + * -7..9: M_LZO1X_999 if ENABLE_LZOP_COMPR_HIGH + */ + if (h->method == M_LZO1X_1) + wrk_mem = xzalloc(LZO1X_1_MEM_COMPRESS); + else /* check only if it's not the only possibility */ + IF_LZOP_COMPR_HIGH(if (h->method == M_LZO1X_1_15)) + wrk_mem = xzalloc(LZO1X_1_15_MEM_COMPRESS); +#if ENABLE_LZOP_COMPR_HIGH + else /* must be h->method == M_LZO1X_999 */ + wrk_mem = xzalloc(LZO1X_999_MEM_COMPRESS); +#endif + + for (;;) { + unsigned src_len, dst_len; + int l; + uint32_t wordbuf[6]; + uint32_t *wordptr = wordbuf; + + /* read a block */ + l = full_read(0, b1, block_size); + src_len = (l > 0 ? l : 0); + + /* write uncompressed block size */ + /* exit if last block */ + if (src_len == 0) { + write32(0); + break; + } + *wordptr++ = htonl(src_len); + + /* compute checksum of uncompressed block */ + if (h->flags32 & F_ADLER32_D) + d_adler32 = lzo_adler32(ADLER32_INIT_VALUE, b1, src_len); + if (h->flags32 & F_CRC32_D) + d_crc32 = lzo_crc32(CRC32_INIT_VALUE, b1, src_len); + + /* compress */ + if (h->method == M_LZO1X_1) + r = lzo1x_1_compress(b1, src_len, b2, &dst_len, wrk_mem); + else IF_LZOP_COMPR_HIGH(if (h->method == M_LZO1X_1_15)) + r = lzo1x_1_15_compress(b1, src_len, b2, &dst_len, wrk_mem); +#if ENABLE_LZOP_COMPR_HIGH + else /* must be h->method == M_LZO1X_999 */ + r = lzo1x_999_compress_level(b1, src_len, b2, &dst_len, + wrk_mem, h->level); +#endif + if (r != 0) /* not LZO_E_OK */ + bb_error_msg_and_die("%s: %s", "internal error", "compression"); + + /* write compressed block size */ + if (dst_len < src_len) { + /* optimize */ + if (h->method == M_LZO1X_999) { + unsigned new_len = src_len; + r = lzo1x_optimize(b2, dst_len, b1, &new_len /*, NULL*/); + if (r != 0 /*LZO_E_OK*/ || new_len != src_len) + bb_error_msg_and_die("%s: %s", "internal error", "optimization"); + } + *wordptr++ = htonl(dst_len); + } else { + /* data actually expanded => store data uncompressed */ + *wordptr++ = htonl(src_len); + } + + /* write checksum of uncompressed block */ + if (h->flags32 & F_ADLER32_D) + *wordptr++ = htonl(d_adler32); + if (h->flags32 & F_CRC32_D) + *wordptr++ = htonl(d_crc32); + + if (dst_len < src_len) { + /* write checksum of compressed block */ + if (h->flags32 & F_ADLER32_C) + *wordptr++ = htonl(lzo_adler32(ADLER32_INIT_VALUE, b2, dst_len)); + if (h->flags32 & F_CRC32_C) + *wordptr++ = htonl(lzo_crc32(CRC32_INIT_VALUE, b2, dst_len)); + } + xwrite(1, wordbuf, ((char*)wordptr) - ((char*)wordbuf)); + if (dst_len < src_len) { + /* write compressed block data */ + xwrite(1, b2, dst_len); + } else { + /* write uncompressed block data */ + xwrite(1, b1, src_len); + } + // /* if full_read() was nevertheless "short", it was EOF */ + // if (src_len < block_size) + // break; + } + + free(wrk_mem); + free(b1); + free(b2); + return 1; +} + +static FAST_FUNC void lzo_check( + uint32_t init, + uint8_t* buf, unsigned len, + uint32_t FAST_FUNC (*fn)(uint32_t, const uint8_t*, unsigned), + uint32_t ref) +{ + /* This function, by having the same order of parameters + * as fn, and by being marked FAST_FUNC (same as fn), + * saves a dozen bytes of code. + */ + uint32_t c = fn(init, buf, len); + if (c != ref) + bb_error_msg_and_die("checksum error"); +} + +/**********************************************************************/ +// decompress a file +/**********************************************************************/ +// used to have "const header_t *h" parameter, but since it uses +// only flags32 field, changed to receive only that. +static NOINLINE int lzo_decompress(uint32_t h_flags32) +{ + unsigned block_size = LZO_BLOCK_SIZE; + int r; + uint32_t src_len, dst_len; + 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; + uint8_t *b1; + uint32_t mcs_block_size = MAX_COMPRESSED_SIZE(block_size); + uint8_t *b2 = NULL; + + for (;;) { + uint8_t *dst; + + /* read uncompressed block size */ + dst_len = read32(); + + /* exit if last block */ + if (dst_len == 0) + break; + + /* error if split file */ + if (dst_len == 0xffffffffL) + /* should not happen - not yet implemented */ + bb_error_msg_and_die("this file is a split lzop file"); + + if (dst_len > MAX_BLOCK_SIZE) + bb_error_msg_and_die("corrupted data"); + + /* read compressed block size */ + src_len = read32(); + if (src_len <= 0 || src_len > dst_len) + bb_error_msg_and_die("corrupted data"); + + if (dst_len > block_size) { + if (b2) { + free(b2); + b2 = NULL; + } + block_size = dst_len; + mcs_block_size = MAX_COMPRESSED_SIZE(block_size); + } + + /* read checksum of uncompressed block */ + if (h_flags32 & F_ADLER32_D) + d_adler32 = read32(); + if (h_flags32 & F_CRC32_D) + d_crc32 = read32(); + + /* read checksum of compressed block */ + if (src_len < dst_len) { + if (h_flags32 & F_ADLER32_C) + c_adler32 = read32(); + if (h_flags32 & F_CRC32_C) + c_crc32 = read32(); + } + + if (b2 == NULL) + b2 = xzalloc(mcs_block_size); + /* read the block into the end of our buffer */ + b1 = b2 + mcs_block_size - src_len; + xread(0, b1, src_len); + + if (src_len < dst_len) { + unsigned d = dst_len; + + if (!(option_mask32 & OPT_F)) { + /* verify checksum of compressed block */ + if (h_flags32 & F_ADLER32_C) + lzo_check(ADLER32_INIT_VALUE, + b1, src_len, + lzo_adler32, c_adler32); + if (h_flags32 & F_CRC32_C) + lzo_check(CRC32_INIT_VALUE, + b1, src_len, + lzo_crc32, c_crc32); + } + + /* decompress */ +// if (option_mask32 & OPT_F) +// r = lzo1x_decompress(b1, src_len, b2, &d /*, NULL*/); +// else + r = lzo1x_decompress_safe(b1, src_len, b2, &d /*, NULL*/); + + if (r != 0 /*LZO_E_OK*/ || dst_len != d) { + bb_error_msg_and_die("corrupted data"); + } + dst = b2; + } else { + /* "stored" block => no decompression */ + dst = b1; + } + + if (!(option_mask32 & OPT_F)) { + /* verify checksum of uncompressed block */ + if (h_flags32 & F_ADLER32_D) + lzo_check(ADLER32_INIT_VALUE, + dst, dst_len, + lzo_adler32, d_adler32); + if (h_flags32 & F_CRC32_D) + lzo_check(CRC32_INIT_VALUE, + dst, dst_len, + lzo_crc32, d_crc32); + } + + /* write uncompressed block data */ + xwrite(1, dst, dst_len); + } + + free(b2); + return 1; +} + +/**********************************************************************/ +// lzop file signature (shamelessly borrowed from PNG) +/**********************************************************************/ +/* + * The first nine bytes of a lzop file always contain the following values: + * + * 0 1 2 3 4 5 6 7 8 + * --- --- --- --- --- --- --- --- --- + * (hex) 89 4c 5a 4f 00 0d 0a 1a 0a + * (decimal) 137 76 90 79 0 13 10 26 10 + * (C notation - ASCII) \211 L Z O \0 \r \n \032 \n + */ + +/* (vda) comparison with lzop v1.02rc1 ("lzop -1 len_and_name+1 + 0; /* 0 is strlen(h->len_and_name+1) */ + /* Store length byte */ + /*h->len_and_name[0] = end - (h->len_and_name+1); - zero already */ + + f_write(&h->version_be16, end - (char*)&h->version_be16); + + h->flags32 = htonl(h->flags32); /* native endianness for lzo_compress() */ + + write32(chksum_getresult(h->flags32)); +} + +static int read_header(header_t *h) +{ + int l; + uint32_t checksum; + /* As it stands now, only h->flags32 is used by our caller. + * Therefore we don't store many fields in h->FIELD. + */ + unsigned h_version; + unsigned h_version_needed_to_extract; + + init_chksum(); + + /* We don't support versions < 0.94, since 0.94 + * came only 2 months after 0.90: + * 0.90 (10 Aug 1997): First public release of lzop + * 0.94 (15 Oct 1997): Header format change + */ + + /* Read up to and including name length byte */ + f_read(&h->version_be16, ((char*)&h->len_and_name[1]) - ((char*)&h->version_be16)); + + h_version = htons(h->version_be16); + if (h_version < 0x0940) + return 3; + h_version_needed_to_extract = htons(h->version_needed_to_extract_be16); + if (h_version_needed_to_extract > LZOP_VERSION) + return 16; + if (h_version_needed_to_extract < 0x0940) + return 3; + + if (h->method <= 0) + return 14; + + /* former lzo_get_method(h): */ + if (h->method == M_LZO1X_1) { + if (h->level == 0) + h->level = 3; + } else if (h->method == M_LZO1X_1_15) { + if (h->level == 0) + h->level = 1; + } else if (h->method == M_LZO1X_999) { + if (h->level == 0) + h->level = 9; + } else + return -1; /* not a LZO method */ + /* check compression level */ + if (h->level < 1 || h->level > 9) + return 15; + + h->flags32 = ntohl(h->flags32); + if (h->flags32 & F_H_FILTER) + return 16; /* filter not supported */ + /* check reserved flags */ + if (h->flags32 & F_RESERVED) + return -13; + + l = h->len_and_name[0]; + if (l > 0) + /* UNUSED */ f_read(h->len_and_name+1, l); + /* UNUSED h->len_and_name[1+l] = 0; */ + + checksum = chksum_getresult(h->flags32); + if (read32() != checksum) + return 2; + + /* skip extra field [not used yet] */ + if (h->flags32 & F_H_EXTRA_FIELD) { + uint32_t extra_field_len; + uint32_t extra_field_checksum; + uint32_t k; + char dummy; + + /* note: the checksum also covers the length */ + init_chksum(); + extra_field_len = f_read32(); + for (k = 0; k < extra_field_len; k++) + f_read(&dummy, 1); + checksum = chksum_getresult(h->flags32); + extra_field_checksum = read32(); + if (extra_field_checksum != checksum) + return 3; + } + + return 0; +} + +/**********************************************************************/ +// compress +/**********************************************************************/ +static void lzo_set_method(header_t *h) +{ + smallint level; + + /* levels 2..6 or none (defaults to level 3) */ + h->method = M_LZO1X_1; + level = 5; /* levels 2-6 are actually the same */ + + if (option_mask32 & OPT_1) { + h->method = M_LZO1X_1_15; + level = 1; + } + if (option_mask32 & OPT_789) { +#if ENABLE_LZOP_COMPR_HIGH + h->method = M_LZO1X_999; + level = 9; + if (option_mask32 & OPT_7) + level = 7; + else if (option_mask32 & OPT_8) + level = 8; +#else + bb_error_msg_and_die("high compression not compiled in"); +#endif + } + + h->level = level; +} + +static int do_lzo_compress(void) +{ + header_t header; + +#define h (&header) + memset(h, 0, sizeof(*h)); + + lzo_set_method(h); + + h->version_be16 = htons(LZOP_VERSION & 0xffff); + h->version_needed_to_extract_be16 = htons(0x0940); + h->lib_version_be16 = htons(lzo_version() & 0xffff); + + h->flags32 = htonl((F_OS & F_OS_MASK) | (F_CS & F_CS_MASK)); + + if (!(option_mask32 & OPT_F) || h->method == M_LZO1X_999) { + h->flags32 |= htonl(F_ADLER32_D); + if (option_mask32 & OPT_C) + h->flags32 |= htonl(F_ADLER32_C); + } + + /* write_header() also converts h->flags32 to native endianness */ + write_header(h); + + return lzo_compress(h); +#undef h +} + +/**********************************************************************/ +// decompress +/**********************************************************************/ +static int do_lzo_decompress(void) +{ + int r; + header_t header; + + check_magic(); + r = read_header(&header); + if (r != 0) + bb_error_msg_and_die("header_error %d", r); + return lzo_decompress(header.flags32); +} + +static char* FAST_FUNC make_new_name_lzop(char *filename, const char *expected_ext UNUSED_PARAM) +{ + if (option_mask32 & OPT_DECOMPRESS) { + char *extension = strrchr(filename, '.'); + if (!extension || strcmp(extension + 1, "lzo") != 0) + return xasprintf("%s.out", filename); + *extension = '\0'; + return filename; + } + return xasprintf("%s.lzo", filename); +} + +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(); + return do_lzo_compress(); +} + +int lzop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int lzop_main(int argc UNUSED_PARAM, char **argv) +{ + INIT_G(); + + getopt32(argv, OPTION_STRING); + argv += optind; + /* -U is "anti -k", invert bit for bbunpack(): */ + option_mask32 ^= OPT_KEEP; + /* -k disables -U (if any): */ + /* opt_complementary "k-U"? - nope, only handles -Uk, not -kU */ + if (option_mask32 & OPT_k) + option_mask32 |= OPT_KEEP; + + /* lzopcat? */ + if (ENABLE_LZOPCAT && applet_name[4] == 'c') + option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS); + /* unlzop? */ + if (ENABLE_UNLZOP && applet_name[4] == 'o') + option_mask32 |= OPT_DECOMPRESS; + + global_crc32_new_table_le(); + return bbunpack(argv, pack_lzop, make_new_name_lzop, /*unused:*/ NULL); +} diff --git a/busybox/archival/rpm.c b/busybox/archival/rpm.c new file mode 100644 index 00000000000000..790eeb59d3ba8a --- /dev/null +++ b/busybox/archival/rpm.c @@ -0,0 +1,559 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rpm applet for busybox + * + * Copyright (C) 2001,2002 by Laurence Anderson + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config RPM +//config: bool "rpm (33 kb)" +//config: default y +//config: help +//config: Mini RPM applet - queries and extracts RPM packages. + +//applet:IF_RPM(APPLET(rpm, BB_DIR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_RPM) += rpm.o + +#include "libbb.h" +#include "common_bufsiz.h" +#include "bb_archive.h" +#include "rpm.h" + +#define RPM_CHAR_TYPE 1 +#define RPM_INT8_TYPE 2 +#define RPM_INT16_TYPE 3 +#define RPM_INT32_TYPE 4 +/* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */ +#define RPM_STRING_TYPE 6 +#define RPM_BIN_TYPE 7 +#define RPM_STRING_ARRAY_TYPE 8 +#define RPM_I18NSTRING_TYPE 9 + +#define TAG_NAME 1000 +#define TAG_VERSION 1001 +#define TAG_RELEASE 1002 +#define TAG_SUMMARY 1004 +#define TAG_DESCRIPTION 1005 +#define TAG_BUILDTIME 1006 +#define TAG_BUILDHOST 1007 +#define TAG_SIZE 1009 +#define TAG_VENDOR 1011 +#define TAG_LICENSE 1014 +#define TAG_PACKAGER 1015 +#define TAG_GROUP 1016 +#define TAG_URL 1020 +#define TAG_PREIN 1023 +#define TAG_POSTIN 1024 +#define TAG_FILEFLAGS 1037 +#define TAG_FILEUSERNAME 1039 +#define TAG_FILEGROUPNAME 1040 +#define TAG_SOURCERPM 1044 +#define TAG_PREINPROG 1085 +#define TAG_POSTINPROG 1086 +#define TAG_PREFIXS 1098 +#define TAG_DIRINDEXES 1116 +#define TAG_BASENAMES 1117 +#define TAG_DIRNAMES 1118 +#define TAG_PAYLOADCOMPRESSOR 1125 + + +#define RPMFILE_CONFIG (1 << 0) +#define RPMFILE_DOC (1 << 1) + +enum rpm_functions_e { + rpm_query = 1, + rpm_install = 2, + rpm_query_info = 4, + rpm_query_package = 8, + rpm_query_list = 16, + rpm_query_list_doc = 32, + rpm_query_list_config = 64 +}; + +typedef struct { + uint32_t tag; /* 4 byte tag */ + uint32_t type; /* 4 byte type */ + uint32_t offset; /* 4 byte offset */ + uint32_t count; /* 4 byte count */ +} rpm_index; + +struct globals { + void *map; + rpm_index *mytags; + int tagcount; + unsigned mapsize, pagesize; +} FIX_ALIASING; +#define G (*(struct globals*)bb_common_bufsiz1) +#define INIT_G() do { setup_common_bufsiz(); } while (0) + +static int rpm_gettags(const char *filename) +{ + rpm_index *tags; + int fd; + unsigned pass, idx; + unsigned storepos; + + if (!filename) { /* rpm2cpio w/o filename? */ + filename = bb_msg_standard_output; + fd = 0; + } else { + fd = xopen(filename, O_RDONLY); + } + + storepos = xlseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */ + G.tagcount = 0; + tags = NULL; + idx = 0; + /* 1st pass is the signature headers, 2nd is the main stuff */ + for (pass = 0; pass < 2; pass++) { + struct rpm_header header; + unsigned cnt; + + xread(fd, &header, sizeof(header)); + if (header.magic_and_ver != htonl(RPM_HEADER_MAGICnVER)) + bb_error_msg_and_die("invalid RPM header magic in '%s'", filename); + header.size = ntohl(header.size); + cnt = ntohl(header.entries); + storepos += sizeof(header) + cnt * 16; + + G.tagcount += cnt; + tags = xrealloc(tags, sizeof(tags[0]) * G.tagcount); + xread(fd, &tags[idx], sizeof(tags[0]) * cnt); + while (cnt--) { + rpm_index *tag = &tags[idx]; + tag->tag = ntohl(tag->tag); + tag->type = ntohl(tag->type); + tag->count = ntohl(tag->count); + tag->offset = storepos + ntohl(tag->offset); + if (pass == 0) + tag->tag -= 743; + idx++; + } + /* Skip padding to 8 byte boundary after reading signature headers */ + if (pass == 0) + while (header.size & 7) + header.size++; + /* Seek past store */ + storepos = xlseek(fd, header.size, SEEK_CUR); + } + G.mytags = tags; + + /* Map the store */ + storepos = (storepos + G.pagesize) & -(int)G.pagesize; + /* remember size for munmap */ + G.mapsize = storepos; + /* some NOMMU systems prefer MAP_PRIVATE over MAP_SHARED */ + G.map = mmap(0, storepos, PROT_READ, MAP_PRIVATE, fd, 0); + if (G.map == MAP_FAILED) + bb_perror_msg_and_die("mmap '%s'", filename); + + return fd; +} + +static int bsearch_rpmtag(const void *key, const void *item) +{ + int *tag = (int *)key; + rpm_index *tmp = (rpm_index *) item; + return (*tag - tmp->tag); +} + +static char *rpm_getstr(int tag, int itemindex) +{ + rpm_index *found; + found = bsearch(&tag, G.mytags, G.tagcount, sizeof(G.mytags[0]), bsearch_rpmtag); + if (!found || itemindex >= found->count) + return NULL; + if (found->type == RPM_STRING_TYPE + || found->type == RPM_I18NSTRING_TYPE + || found->type == RPM_STRING_ARRAY_TYPE + ) { + int n; + char *tmpstr = (char *) G.map + found->offset; + for (n = 0; n < itemindex; n++) + tmpstr = tmpstr + strlen(tmpstr) + 1; + return tmpstr; + } + return NULL; +} +static char *rpm_getstr0(int tag) +{ + return rpm_getstr(tag, 0); +} + +#if ENABLE_RPM + +static int rpm_getint(int tag, int itemindex) +{ + rpm_index *found; + char *tmpint; + + /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ... + * it's ok to ignore it because tag won't be used as a pointer */ + found = bsearch(&tag, G.mytags, G.tagcount, sizeof(G.mytags[0]), bsearch_rpmtag); + if (!found || itemindex >= found->count) + return -1; + + tmpint = (char *) G.map + found->offset; + if (found->type == RPM_INT32_TYPE) { + tmpint += itemindex*4; + return ntohl(*(int32_t*)tmpint); + } + if (found->type == RPM_INT16_TYPE) { + tmpint += itemindex*2; + return ntohs(*(int16_t*)tmpint); + } + if (found->type == RPM_INT8_TYPE) { + tmpint += itemindex; + return *(int8_t*)tmpint; + } + return -1; +} + +static int rpm_getcount(int tag) +{ + rpm_index *found; + found = bsearch(&tag, G.mytags, G.tagcount, sizeof(G.mytags[0]), bsearch_rpmtag); + if (!found) + return 0; + return found->count; +} + +static void fileaction_dobackup(char *filename, int fileref) +{ + struct stat oldfile; + int stat_res; + char *newname; + if (rpm_getint(TAG_FILEFLAGS, fileref) & RPMFILE_CONFIG) { + /* Only need to backup config files */ + stat_res = lstat(filename, &oldfile); + if (stat_res == 0 && S_ISREG(oldfile.st_mode)) { + /* File already exists - really should check MD5's etc to see if different */ + newname = xasprintf("%s.rpmorig", filename); + copy_file(filename, newname, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS); + remove_file(filename, FILEUTILS_RECUR | FILEUTILS_FORCE); + free(newname); + } + } +} + +static void fileaction_setowngrp(char *filename, int fileref) +{ + /* real rpm warns: "user foo does not exist - using " */ + struct passwd *pw = getpwnam(rpm_getstr(TAG_FILEUSERNAME, fileref)); + int uid = pw ? pw->pw_uid : getuid(); /* or euid? */ + struct group *gr = getgrnam(rpm_getstr(TAG_FILEGROUPNAME, fileref)); + int gid = gr ? gr->gr_gid : getgid(); + chown(filename, uid, gid); +} + +static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref)) +{ + int count = 0; + while (rpm_getstr(filetag, count)) { + char* filename = xasprintf("%s%s", + rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, count)), + rpm_getstr(TAG_BASENAMES, count)); + fileaction(filename, count++); + free(filename); + } +} + +#if 0 //DEBUG +static void print_all_tags(void) +{ + unsigned i = 0; + while (i < G.tagcount) { + rpm_index *tag = &G.mytags[i]; + if (tag->type == RPM_STRING_TYPE + || tag->type == RPM_I18NSTRING_TYPE + || tag->type == RPM_STRING_ARRAY_TYPE + ) { + unsigned n; + char *str = (char *) G.map + tag->offset; + + printf("tag[%u] %08x type %08x offset %08x count %d '%s'\n", + i, tag->tag, tag->type, tag->offset, tag->count, str + ); + for (n = 1; n < tag->count; n++) { + str += strlen(str) + 1; + printf("\t'%s'\n", str); + } + } + i++; + } +} +#else +#define print_all_tags() ((void)0) +#endif + +static void extract_cpio(int fd, const char *source_rpm) +{ + archive_handle_t *archive_handle; + + if (source_rpm != NULL) { + /* Binary rpm (it was built from some SRPM), install to root */ + xchdir("/"); + } /* else: SRPM, install to current dir */ + + /* Initialize */ + archive_handle = init_handle(); + archive_handle->seek = seek_by_read; + archive_handle->action_data = data_extract_all; +#if 0 /* For testing (rpm -i only lists the files in internal cpio): */ + archive_handle->action_header = header_list; + archive_handle->action_data = data_skip; +#endif + archive_handle->ah_flags = ARCHIVE_RESTORE_DATE | ARCHIVE_CREATE_LEADING_DIRS + /* compat: overwrite existing files. + * try "rpm -i foo.src.rpm" few times in a row - + * standard rpm will not complain. + */ + | ARCHIVE_REPLACE_VIA_RENAME; + archive_handle->src_fd = fd; + /*archive_handle->offset = 0; - init_handle() did it */ + + setup_unzip_on_fd(archive_handle->src_fd, /*fail_if_not_compressed:*/ 1); + while (get_header_cpio(archive_handle) == EXIT_SUCCESS) + continue; +} + +//usage:#define rpm_trivial_usage +//usage: "-i PACKAGE.rpm; rpm -qp[ildc] PACKAGE.rpm" +//usage:#define rpm_full_usage "\n\n" +//usage: "Manipulate RPM packages\n" +//usage: "\nCommands:" +//usage: "\n -i Install package" +//usage: "\n -qp Query package" +//usage: "\n -qpi Show information" +//usage: "\n -qpl List contents" +//usage: "\n -qpd List documents" +//usage: "\n -qpc List config files" + +/* RPM version 4.13.0.1: + * Unlike -q, -i seems to imply -p: -i, -ip and -pi work the same. + * OTOH, with -q order is important: "-piq FILE.rpm" works as -qp, not -qpi + * (IOW: shows only package name, not package info). + * "-iq ARG" works as -q: treats ARG as package name, not a file. + * + * "man rpm" on -l option and options implying it: + * -l, --list List files in package. + * -c, --configfiles List only configuration files (implies -l). + * -d, --docfiles List only documentation files (implies -l). + * -L, --licensefiles List only license files (implies -l). + * --dump Dump file information as follows (implies -l): + * path size mtime digest mode owner group isconfig isdoc rdev symlink + * -s, --state Display the states of files in the package (implies -l). + * The state of each file is one of normal, not installed, or replaced. + * + * Looks like we can switch to getopt32 here: in practice, people + * do place -q first if they intend to use it (misinterpreting "-piq" wouldn't matter). + */ +int rpm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int rpm_main(int argc, char **argv) +{ + int opt, func = 0; + + INIT_G(); + G.pagesize = getpagesize(); + + while ((opt = getopt(argc, argv, "iqpldc")) != -1) { + switch (opt) { + case 'i': /* First arg: Install mode, with q: Information */ + if (!func) func = rpm_install; + else func |= rpm_query_info; + break; + case 'q': /* First arg: Query mode */ + if (func) bb_show_usage(); + func = rpm_query; + break; + case 'p': /* Query a package (IOW: .rpm file, we are not querying RPMDB) */ + func |= rpm_query_package; + break; + case 'l': /* List files in a package */ + func |= rpm_query_list; + break; + case 'd': /* List doc files in a package (implies -l) */ + func |= rpm_query_list; + func |= rpm_query_list_doc; + break; + case 'c': /* List config files in a package (implies -l) */ + func |= rpm_query_list; + func |= rpm_query_list_config; + break; + default: + bb_show_usage(); + } + } + argv += optind; + //argc -= optind; + if (!argv[0]) { + bb_show_usage(); + } + + for (;;) { + int rpm_fd; + const char *source_rpm; + + rpm_fd = rpm_gettags(*argv); + print_all_tags(); + + source_rpm = rpm_getstr0(TAG_SOURCERPM); + + if (func & rpm_install) { + /* -i (and not -qi) */ + + /* Backup any config files */ + loop_through_files(TAG_BASENAMES, fileaction_dobackup); + /* Extact the archive */ + extract_cpio(rpm_fd, source_rpm); + /* Set the correct file uid/gid's */ + loop_through_files(TAG_BASENAMES, fileaction_setowngrp); + } + else + if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) { + /* -qp */ + + if (!(func & (rpm_query_info|rpm_query_list))) { + /* If just a straight query, just give package name */ + printf("%s-%s-%s\n", rpm_getstr0(TAG_NAME), rpm_getstr0(TAG_VERSION), rpm_getstr0(TAG_RELEASE)); + } + if (func & rpm_query_info) { + /* Do the nice printout */ + time_t bdate_time; + struct tm *bdate_ptm; + char bdatestring[50]; + const char *p; + + printf("%-12s: %s\n", "Name" , rpm_getstr0(TAG_NAME)); + /* TODO compat: add "Epoch" here */ + printf("%-12s: %s\n", "Version" , rpm_getstr0(TAG_VERSION)); + printf("%-12s: %s\n", "Release" , rpm_getstr0(TAG_RELEASE)); + /* add "Architecture" */ + /* printf("%-12s: %s\n", "Install Date", "(not installed)"); - we don't know */ + printf("%-12s: %s\n", "Group" , rpm_getstr0(TAG_GROUP)); + printf("%-12s: %d\n", "Size" , rpm_getint(TAG_SIZE, 0)); + printf("%-12s: %s\n", "License" , rpm_getstr0(TAG_LICENSE)); + /* add "Signature" */ + printf("%-12s: %s\n", "Source RPM" , source_rpm ? source_rpm : "(none)"); + bdate_time = rpm_getint(TAG_BUILDTIME, 0); + bdate_ptm = localtime(&bdate_time); + strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm); + printf("%-12s: %s\n", "Build Date" , bdatestring); + printf("%-12s: %s\n", "Build Host" , rpm_getstr0(TAG_BUILDHOST)); + p = rpm_getstr0(TAG_PREFIXS); + printf("%-12s: %s\n", "Relocations" , p ? p : "(not relocatable)"); + /* add "Packager" */ + p = rpm_getstr0(TAG_VENDOR); + if (p) /* rpm 4.13.0.1 does not show "(none)" for Vendor: */ + printf("%-12s: %s\n", "Vendor" , p); + p = rpm_getstr0(TAG_URL); + if (p) /* rpm 4.13.0.1 does not show "(none)"/"(null)" for URL: */ + printf("%-12s: %s\n", "URL" , p); + printf("%-12s: %s\n", "Summary" , rpm_getstr0(TAG_SUMMARY)); + printf("Description :\n%s\n", rpm_getstr0(TAG_DESCRIPTION)); + } + if (func & rpm_query_list) { + int count, it, flags; + count = rpm_getcount(TAG_BASENAMES); + for (it = 0; it < count; it++) { + flags = rpm_getint(TAG_FILEFLAGS, it); + switch (func & (rpm_query_list_doc|rpm_query_list_config)) { + case rpm_query_list_doc: + if (!(flags & RPMFILE_DOC)) continue; + break; + case rpm_query_list_config: + if (!(flags & RPMFILE_CONFIG)) continue; + break; + case rpm_query_list_doc|rpm_query_list_config: + if (!(flags & (RPMFILE_CONFIG|RPMFILE_DOC))) continue; + break; + } + printf("%s%s\n", + rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, it)), + rpm_getstr(TAG_BASENAMES, it)); + } + } + } else { + /* Unsupported (help text shows what we support) */ + bb_show_usage(); + } + if (!*++argv) + break; + munmap(G.map, G.mapsize); + free(G.mytags); + close(rpm_fd); + } + + return 0; +} + +#endif /* RPM */ + +/* + * Mini rpm2cpio implementation for busybox + * + * Copyright (C) 2001 by Laurence Anderson + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config RPM2CPIO +//config: bool "rpm2cpio (20 kb)" +//config: default y +//config: help +//config: Converts a RPM file into a CPIO archive. + +//applet:IF_RPM2CPIO(APPLET(rpm2cpio, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_RPM2CPIO) += rpm.o + +//usage:#define rpm2cpio_trivial_usage +//usage: "PACKAGE.rpm" +//usage:#define rpm2cpio_full_usage "\n\n" +//usage: "Output a cpio archive of the rpm file" + +#if ENABLE_RPM2CPIO + +/* No getopt required */ +int rpm2cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int rpm2cpio_main(int argc UNUSED_PARAM, char **argv) +{ + const char *str; + int rpm_fd; + + INIT_G(); + G.pagesize = getpagesize(); + + rpm_fd = rpm_gettags(argv[1]); + + //if (SEAMLESS_COMPRESSION) - we do this at the end instead. + // /* We need to know whether child (gzip/bzip/etc) exits abnormally */ + // signal(SIGCHLD, check_errors_in_children); + + if (ENABLE_FEATURE_SEAMLESS_LZMA + && (str = rpm_getstr0(TAG_PAYLOADCOMPRESSOR)) != NULL + && strcmp(str, "lzma") == 0 + ) { + // lzma compression can't be detected + // set up decompressor without detection + setup_lzma_on_fd(rpm_fd); + } else { + setup_unzip_on_fd(rpm_fd, /*fail_if_not_compressed:*/ 1); + } + + if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0) + bb_error_msg_and_die("error unpacking"); + + if (ENABLE_FEATURE_CLEAN_UP) { + close(rpm_fd); + } + + if (SEAMLESS_COMPRESSION) { + check_errors_in_children(0); + return bb_got_signal; + } + return EXIT_SUCCESS; +} + +#endif /* RPM2CPIO */ diff --git a/busybox/archival/rpm.h b/busybox/archival/rpm.h new file mode 100644 index 00000000000000..afe2b550c8d808 --- /dev/null +++ b/busybox/archival/rpm.h @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * RPM structs and consts + * + * Copyright (C) 2001 by Laurence Anderson + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* RPM file starts with this struct: */ +struct rpm_lead { + uint32_t magic; + uint8_t major, minor; + uint16_t type; + uint16_t archnum; + char name[66]; + uint16_t osnum; + uint16_t signature_type; + char reserved[16]; +}; +struct BUG_rpm_lead { + char bug[sizeof(struct rpm_lead) == 96 ? 1 : -1]; +}; +#define RPM_LEAD_MAGIC 0xedabeedb +#define RPM_LEAD_MAGIC_STR "\355\253\356\333" + +/* Then follows the header: */ +struct rpm_header { + uint32_t magic_and_ver; /* 3 byte magic: 0x8e 0xad 0xe8; 1 byte version: 0x01 */ + uint32_t reserved; /* 4 bytes reserved */ + uint32_t entries; /* Number of entries in header (4 bytes) */ + uint32_t size; /* Size of store (4 bytes) */ +}; +struct BUG_rpm_header { + char bug[sizeof(struct rpm_header) == 16 ? 1 : -1]; +}; +#define RPM_HEADER_MAGICnVER 0x8eade801 +#define RPM_HEADER_MAGIC_STR "\216\255\350" diff --git a/busybox/archival/tar.c b/busybox/archival/tar.c new file mode 100644 index 00000000000000..ae1ccc756c8651 --- /dev/null +++ b/busybox/archival/tar.c @@ -0,0 +1,1268 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tar implementation for busybox + * + * Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg + * by Glenn McGrath + * + * Note, that as of BusyBox-0.43, tar has been completely rewritten from the + * ground up. It still has remnants of the old code lying about, but it is + * very different now (i.e., cleaner, less global variables, etc.) + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Based in part in the tar implementation in sash + * Copyright (c) 1999 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * Permission to distribute sash derived code under GPL has been granted. + * + * Based in part on the tar implementation from busybox-0.28 + * Copyright (C) 1995 Bruce Perens + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config TAR +//config: bool "tar (40 kb)" +//config: default y +//config: help +//config: tar is an archiving program. It's commonly used with gzip to +//config: create compressed archives. It's probably the most widely used +//config: UNIX archive program. +//config: +//config:config FEATURE_TAR_LONG_OPTIONS +//config: bool "Enable long options" +//config: default y +//config: depends on TAR && LONG_OPTS +//config: +//config:config FEATURE_TAR_CREATE +//config: bool "Enable -c (archive creation)" +//config: default y +//config: depends on TAR +//config: +//config:config FEATURE_TAR_AUTODETECT +//config: bool "Autodetect compressed tarballs" +//config: default y +//config: depends on TAR && (FEATURE_SEAMLESS_Z || FEATURE_SEAMLESS_GZ || FEATURE_SEAMLESS_BZ2 || FEATURE_SEAMLESS_LZMA || FEATURE_SEAMLESS_XZ) +//config: help +//config: With this option tar can automatically detect compressed +//config: tarballs. Currently it works only on files (not pipes etc). +//config: +//config:config FEATURE_TAR_FROM +//config: bool "Enable -X (exclude from) and -T (include from) options" +//config: default y +//config: depends on TAR +//config: help +//config: If you enable this option you'll be able to specify +//config: a list of files to include or exclude from an archive. +//config: +//config:config FEATURE_TAR_OLDGNU_COMPATIBILITY +//config: bool "Support old tar header format" +//config: default y +//config: depends on TAR || DPKG +//config: help +//config: This option is required to unpack archives created in +//config: the old GNU format; help to kill this old format by +//config: repacking your ancient archives with the new format. +//config: +//config:config FEATURE_TAR_OLDSUN_COMPATIBILITY +//config: bool "Enable untarring of tarballs with checksums produced by buggy Sun tar" +//config: default y +//config: depends on TAR || DPKG +//config: help +//config: This option is required to unpack archives created by some old +//config: version of Sun's tar (it was calculating checksum using signed +//config: arithmetic). It is said to be fixed in newer Sun tar, but "old" +//config: tarballs still exist. +//config: +//config:config FEATURE_TAR_GNU_EXTENSIONS +//config: bool "Support GNU tar extensions (long filenames)" +//config: default y +//config: depends on TAR || DPKG +//config: +//config:config FEATURE_TAR_TO_COMMAND +//config: bool "Support writing to an external program (--to-command)" +//config: default y +//config: depends on TAR && FEATURE_TAR_LONG_OPTIONS +//config: help +//config: If you enable this option you'll be able to instruct tar to send +//config: the contents of each extracted file to the standard input of an +//config: external program. +//config: +//config:config FEATURE_TAR_UNAME_GNAME +//config: bool "Enable use of user and group names" +//config: default y +//config: depends on TAR +//config: help +//config: Enable use of user and group names in tar. This affects contents +//config: listings (-t) and preserving permissions when unpacking (-p). +//config: +200 bytes. +//config: +//config:config FEATURE_TAR_NOPRESERVE_TIME +//config: bool "Enable -m (do not preserve time) GNU option" +//config: default y +//config: depends on TAR +//config: +//config:config FEATURE_TAR_SELINUX +//config: bool "Support extracting SELinux labels" +//config: default n +//config: depends on TAR && SELINUX +//config: help +//config: With this option busybox supports restoring SELinux labels +//config: when extracting files from tar archives. + +//applet:IF_TAR(APPLET(tar, BB_DIR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_TAR) += tar.o + +#include +#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 + +#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 +#define INIT_G() do { setup_common_bufsiz(); } while (0) + + +#if ENABLE_FEATURE_TAR_CREATE + +/* +** writeTarFile(), writeFileToTarball(), and writeTarHeader() are +** the only functions that deal with the HardLinkInfo structure. +** Even these functions use the xxxHardLinkInfo() functions. +*/ +typedef struct HardLinkInfo { + struct HardLinkInfo *next; /* Next entry in list */ + dev_t dev; /* Device number */ + ino_t ino; /* Inode number */ +// short linkCount; /* (Hard) Link Count */ + char name[1]; /* Start of filename (must be last) */ +} HardLinkInfo; + +/* Some info to be carried along when creating a new tarball */ +typedef struct TarBallInfo { + int tarFd; /* Open-for-write file descriptor + * for the tarball */ + int verboseFlag; /* Whether to print extra stuff or not */ +# if ENABLE_FEATURE_TAR_FROM + const llist_t *excludeList; /* List of files to not include */ +# endif + HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ + HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ +//TODO: save only st_dev + st_ino + struct stat tarFileStatBuf; /* Stat info for the tarball, letting + * us know the inode and device that the + * tarball lives, so we can avoid trying + * to include the tarball into itself */ +} TarBallInfo; + +/* A nice enum with all the possible tar file content types */ +enum { + REGTYPE = '0', /* regular file */ + REGTYPE0 = '\0', /* regular file (ancient bug compat) */ + LNKTYPE = '1', /* hard link */ + SYMTYPE = '2', /* symbolic link */ + CHRTYPE = '3', /* character special */ + BLKTYPE = '4', /* block special */ + DIRTYPE = '5', /* directory */ + FIFOTYPE = '6', /* FIFO special */ + CONTTYPE = '7', /* reserved */ + GNULONGLINK = 'K', /* GNU long (>100 chars) link name */ + GNULONGNAME = 'L', /* GNU long (>100 chars) file name */ +}; + +/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ +static void addHardLinkInfo(HardLinkInfo **hlInfoHeadPtr, + struct stat *statbuf, + const char *fileName) +{ + /* Note: hlInfoHeadPtr can never be NULL! */ + HardLinkInfo *hlInfo; + + hlInfo = xmalloc(sizeof(HardLinkInfo) + strlen(fileName)); + hlInfo->next = *hlInfoHeadPtr; + *hlInfoHeadPtr = hlInfo; + hlInfo->dev = statbuf->st_dev; + hlInfo->ino = statbuf->st_ino; +// hlInfo->linkCount = statbuf->st_nlink; + strcpy(hlInfo->name, fileName); +} + +static void freeHardLinkInfo(HardLinkInfo **hlInfoHeadPtr) +{ + HardLinkInfo *hlInfo; + HardLinkInfo *hlInfoNext; + + if (hlInfoHeadPtr) { + hlInfo = *hlInfoHeadPtr; + while (hlInfo) { + hlInfoNext = hlInfo->next; + free(hlInfo); + hlInfo = hlInfoNext; + } + *hlInfoHeadPtr = NULL; + } +} + +/* Might be faster (and bigger) if the dev/ino were stored in numeric order ;) */ +static HardLinkInfo *findHardLinkInfo(HardLinkInfo *hlInfo, struct stat *statbuf) +{ + while (hlInfo) { + if (statbuf->st_ino == hlInfo->ino + && statbuf->st_dev == hlInfo->dev + ) { + DBG("found hardlink:'%s'", hlInfo->name); + break; + } + hlInfo = hlInfo->next; + } + return hlInfo; +} + +/* Put an octal string into the specified buffer. + * The number is zero padded and possibly null terminated. + * Stores low-order bits only if whole value does not fit. */ +static void putOctal(char *cp, int len, off_t value) +{ + char tempBuffer[sizeof(off_t)*3 + 1]; + char *tempString = tempBuffer; + int width; + + width = sprintf(tempBuffer, "%0*"OFF_FMT"o", len, value); + tempString += (width - len); + + /* If string has leading zeroes, we can drop one */ + /* and field will have trailing '\0' */ + /* (increases chances of compat with other tars) */ + if (tempString[0] == '0') + tempString++; + + /* Copy the string to the field */ + memcpy(cp, tempString, len); +} +#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b)) + +static void chksum_and_xwrite(int fd, struct tar_header_t* hp) +{ + /* POSIX says that checksum is done on unsigned bytes + * (Sun and HP-UX gets it wrong... more details in + * GNU tar source) */ + const unsigned char *cp; + int chksum, size; + + strcpy(hp->magic, "ustar "); + + /* Calculate and store the checksum (i.e., the sum of all of the bytes of + * the header). The checksum field must be filled with blanks for the + * calculation. The checksum field is formatted differently from the + * other fields: it has 6 digits, a null, then a space -- rather than + * digits, followed by a null like the other fields... */ + memset(hp->chksum, ' ', sizeof(hp->chksum)); + cp = (const unsigned char *) hp; + chksum = 0; + size = sizeof(*hp); + do { chksum += *cp++; } while (--size); + putOctal(hp->chksum, sizeof(hp->chksum)-1, chksum); + + /* Now write the header out to disk */ + xwrite(fd, hp, sizeof(*hp)); +} + +# if ENABLE_FEATURE_TAR_GNU_EXTENSIONS +static void writeLongname(int fd, int type, const char *name, int dir) +{ + static const struct { + char mode[8]; /* 100-107 */ + char uid[8]; /* 108-115 */ + char gid[8]; /* 116-123 */ + char size[12]; /* 124-135 */ + char mtime[12]; /* 136-147 */ + } prefilled = { + "0000000", + "0000000", + "0000000", + "00000000000", + "00000000000", + }; + struct tar_header_t header; + int size; + + dir = !!dir; /* normalize: 0/1 */ + size = strlen(name) + 1 + dir; /* GNU tar uses strlen+1 */ + /* + dir: account for possible '/' */ + + memset(&header, 0, sizeof(header)); + strcpy(header.name, "././@LongLink"); + memcpy(header.mode, prefilled.mode, sizeof(prefilled)); + PUT_OCTAL(header.size, size); + header.typeflag = type; + chksum_and_xwrite(fd, &header); + + /* Write filename[/] and pad the block. */ + /* dir=0: writes 'name', pads */ + /* dir=1: writes 'name', writes '/', pads */ + dir *= 2; + xwrite(fd, name, size - dir); + xwrite(fd, "/", dir); + size = (-size) & (TAR_BLOCK_SIZE-1); + memset(&header, 0, size); + xwrite(fd, &header, size); +} +# 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) +{ + struct tar_header_t header; + + memset(&header, 0, sizeof(header)); + + strncpy(header.name, header_name, sizeof(header.name)); + + /* POSIX says to mask mode with 07777. */ + PUT_OCTAL(header.mode, statbuf->st_mode & 07777); + 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 */ + /* 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)); + safe_strncpy(header.gname, get_cached_groupname(statbuf->st_gid), sizeof(header.gname)); + + if (tbInfo->hlInfo) { + /* This is a hard link */ + header.typeflag = LNKTYPE; + strncpy(header.linkname, tbInfo->hlInfo->name, + sizeof(header.linkname)); +# if ENABLE_FEATURE_TAR_GNU_EXTENSIONS + /* Write out long linkname if needed */ + if (header.linkname[sizeof(header.linkname)-1]) + writeLongname(tbInfo->tarFd, GNULONGLINK, + tbInfo->hlInfo->name, 0); +# endif + } else if (S_ISLNK(statbuf->st_mode)) { + char *lpath = xmalloc_readlink_or_warn(fileName); + if (!lpath) + return FALSE; + header.typeflag = SYMTYPE; + strncpy(header.linkname, lpath, sizeof(header.linkname)); +# if ENABLE_FEATURE_TAR_GNU_EXTENSIONS + /* Write out long linkname if needed */ + if (header.linkname[sizeof(header.linkname)-1]) + writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0); +# else + /* If it is larger than 100 bytes, bail out */ + if (header.linkname[sizeof(header.linkname)-1]) { + free(lpath); + bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); + return FALSE; + } +# endif + free(lpath); + } else if (S_ISDIR(statbuf->st_mode)) { + header.typeflag = DIRTYPE; + /* Append '/' only if there is a space for it */ + if (!header.name[sizeof(header.name)-1]) + header.name[strlen(header.name)] = '/'; + } else if (S_ISCHR(statbuf->st_mode)) { + header.typeflag = CHRTYPE; + PUT_OCTAL(header.devmajor, major(statbuf->st_rdev)); + PUT_OCTAL(header.devminor, minor(statbuf->st_rdev)); + } else if (S_ISBLK(statbuf->st_mode)) { + header.typeflag = BLKTYPE; + PUT_OCTAL(header.devmajor, major(statbuf->st_rdev)); + PUT_OCTAL(header.devminor, minor(statbuf->st_rdev)); + } else if (S_ISFIFO(statbuf->st_mode)) { + header.typeflag = FIFOTYPE; + } else if (S_ISREG(statbuf->st_mode)) { + /* header.size field is 12 bytes long */ + /* Does octal-encoded size fit? */ + uoff_t filesize = statbuf->st_size; + if (sizeof(filesize) <= 4 + || filesize <= (uoff_t)0777777777777LL + ) { + PUT_OCTAL(header.size, filesize); + } + /* Does base256-encoded size fit? + * It always does unless off_t is wider than 64 bits. + */ + else if (ENABLE_FEATURE_TAR_GNU_EXTENSIONS +# if ULLONG_MAX > 0xffffffffffffffffLL /* 2^64-1 */ + && (filesize <= 0x3fffffffffffffffffffffffLL) +# endif + ) { + /* GNU tar uses "base-256 encoding" for very large numbers. + * Encoding is binary, with highest bit always set as a marker + * and sign in next-highest bit: + * 80 00 .. 00 - zero + * bf ff .. ff - largest positive number + * ff ff .. ff - minus 1 + * c0 00 .. 00 - smallest negative number + */ + char *p8 = header.size + sizeof(header.size); + do { + *--p8 = (uint8_t)filesize; + filesize >>= 8; + } while (p8 != header.size); + *p8 |= 0x80; + } else { + bb_error_msg_and_die("can't store file '%s' " + "of size %"OFF_FMT"u, aborting", + fileName, statbuf->st_size); + } + header.typeflag = REGTYPE; + } else { + bb_error_msg("%s: unknown file type", fileName); + return FALSE; + } + +# if ENABLE_FEATURE_TAR_GNU_EXTENSIONS + /* Write out long name if needed */ + /* (we, like GNU tar, output long linkname *before* long name) */ + if (header.name[sizeof(header.name)-1]) + writeLongname(tbInfo->tarFd, GNULONGNAME, + header_name, S_ISDIR(statbuf->st_mode)); +# endif + + /* Now write the header out to disk */ + chksum_and_xwrite(tbInfo->tarFd, &header); + + /* Now do the verbose thing (or not) */ + if (tbInfo->verboseFlag) { + FILE *vbFd = stdout; + + /* If archive goes to stdout, verbose goes to stderr */ + if (tbInfo->tarFd == STDOUT_FILENO) + vbFd = stderr; + /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */ + /* We don't have such excesses here: for us "v" == "vv" */ + /* '/' is probably a GNUism */ + fprintf(vbFd, "%s%s\n", header_name, + S_ISDIR(statbuf->st_mode) ? "/" : ""); + } + + return TRUE; +} + +# if ENABLE_FEATURE_TAR_FROM +static int exclude_file(const llist_t *excluded_files, const char *file) +{ + while (excluded_files) { + if (excluded_files->data[0] == '/') { + if (fnmatch(excluded_files->data, file, + FNM_PATHNAME | FNM_LEADING_DIR) == 0) + return 1; + } else { + const char *p; + + for (p = file; p[0] != '\0'; p++) { + if ((p == file || p[-1] == '/') + && p[0] != '/' + && fnmatch(excluded_files->data, p, + FNM_PATHNAME | FNM_LEADING_DIR) == 0 + ) { + return 1; + } + } + } + excluded_files = excluded_files->link; + } + + return 0; +} +# else +# define exclude_file(excluded_files, file) 0 +# endif + +static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf, + void *userData, int depth UNUSED_PARAM) +{ + struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; + const char *header_name; + int inputFileFd = -1; + + DBG("writeFileToTarball('%s')", fileName); + + /* Strip leading '/' and such (must be before memorizing hardlink's name) */ + header_name = strip_unsafe_prefix(fileName); + + if (header_name[0] == '\0') + return TRUE; + + /* It is against the rules to archive a socket */ + if (S_ISSOCK(statbuf->st_mode)) { + bb_error_msg("%s: socket ignored", fileName); + return TRUE; + } + + /* + * Check to see if we are dealing with a hard link. + * If so - + * Treat the first occurrence of a given dev/inode as a file while + * treating any additional occurrences as hard links. This is done + * by adding the file information to the HardLinkInfo linked list. + */ + tbInfo->hlInfo = NULL; + if (!S_ISDIR(statbuf->st_mode) && statbuf->st_nlink > 1) { + DBG("'%s': st_nlink > 1", header_name); + tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf); + if (tbInfo->hlInfo == NULL) { + DBG("'%s': addHardLinkInfo", header_name); + addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, header_name); + } + } + + /* It is a bad idea to store the archive we are in the process of creating, + * so check the device and inode to be sure that this particular file isn't + * the new tarball */ + if (tbInfo->tarFileStatBuf.st_dev == statbuf->st_dev + && tbInfo->tarFileStatBuf.st_ino == statbuf->st_ino + ) { + bb_error_msg("%s: file is the archive; skipping", fileName); + return TRUE; + } + + if (exclude_file(tbInfo->excludeList, header_name)) + return SKIP; + +# if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS + if (strlen(header_name) >= NAME_SIZE) { + bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); + return TRUE; + } +# endif + + /* Is this a regular file? */ + if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) { + /* open the file we want to archive, and make sure all is well */ + inputFileFd = open_or_warn(fileName, O_RDONLY); + if (inputFileFd < 0) { + return FALSE; + } + } + + /* Add an entry to the tarball */ + if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) { + return FALSE; + } + + /* If it was a regular file, write out the body */ + if (inputFileFd >= 0) { + size_t readSize; + /* Write the file to the archive. */ + /* We record size into header first, */ + /* and then write out file. If file shrinks in between, */ + /* tar will be corrupted. So we don't allow for that. */ + /* NB: GNU tar 1.16 warns and pads with zeroes */ + /* or even seeks back and updates header */ + bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); + ////off_t readSize; + ////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); + ////if (readSize != statbuf->st_size && readSize >= 0) { + //// bb_error_msg_and_die("short read from %s, aborting", fileName); + ////} + + /* Check that file did not grow in between? */ + /* if (safe_read(inputFileFd, 1) == 1) warn but continue? */ + + close(inputFileFd); + + /* Pad the file up to the tar block size */ + /* (a few tricks here in the name of code size) */ + readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1); + memset(block_buf, 0, readSize); + xwrite(tbInfo->tarFd, block_buf, readSize); + } + + return TRUE; +} + +# if SEAMLESS_COMPRESSION +/* Don't inline: vfork scares gcc and pessimizes code */ +static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) +{ + pid_t gzipPid; + + // 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 + volatile int vfork_exec_errno = 0; + struct fd_pair gzipDataPipe; +# if WAIT_FOR_CHILD + struct fd_pair gzipStatusPipe; + xpiped_pair(gzipStatusPipe); +# endif + xpiped_pair(gzipDataPipe); + + signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ + + gzipPid = xvfork(); + + if (gzipPid == 0) { + /* child */ + /* NB: close _first_, then move fds! */ + close(gzipDataPipe.wr); +# if WAIT_FOR_CHILD + close(gzipStatusPipe.rd); + /* gzipStatusPipe.wr will close only on exec - + * parent waits for this close to happen */ + fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC); +# endif + xmove_fd(gzipDataPipe.rd, 0); + xmove_fd(tar_fd, 1); + /* exec gzip/bzip2 program/applet */ + BB_EXECLP(gzip, gzip, "-f", (char *)0); + vfork_exec_errno = errno; + _exit(EXIT_FAILURE); + } + + /* parent */ + xmove_fd(gzipDataPipe.wr, tar_fd); + close(gzipDataPipe.rd); +# if WAIT_FOR_CHILD + close(gzipStatusPipe.wr); + while (1) { + char buf; + int n; + + /* Wait until child execs (or fails to) */ + n = full_read(gzipStatusPipe.rd, &buf, 1); + if (n < 0 /* && errno == EAGAIN */) + continue; /* try it again */ + } + close(gzipStatusPipe.rd); +# endif + if (vfork_exec_errno) { + errno = vfork_exec_errno; + bb_perror_msg_and_die("can't execute '%s'", gzip); + } +} +# endif /* SEAMLESS_COMPRESSION */ + + +# if !SEAMLESS_COMPRESSION +/* Do not pass gzip flag to writeTarFile() */ +#define writeTarFile(tbInfo, recurseFlags, filelist, gzip) \ + writeTarFile(tbInfo, recurseFlags, filelist) +# endif +/* gcc 4.2.1 inlines it, making code bigger */ +static NOINLINE int writeTarFile( + struct TarBallInfo *tbInfo, + int recurseFlags, + const llist_t *filelist, + const char *gzip) +{ + int errorFlag = FALSE; + + /*tbInfo->hlInfoHead = NULL; - already is */ + + /* 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 SEAMLESS_COMPRESSION + if (gzip) + vfork_compressor(tbInfo->tarFd, gzip); +# endif + + /* Read the directory/files and iterate over them one at a time */ + while (filelist) { + if (!recursive_action(filelist->data, recurseFlags, + writeFileToTarball, writeFileToTarball, tbInfo, 0) + ) { + errorFlag = TRUE; + } + filelist = filelist->link; + } + /* Write two empty blocks to the end of the archive */ + memset(block_buf, 0, 2*TAR_BLOCK_SIZE); + xwrite(tbInfo->tarFd, block_buf, 2*TAR_BLOCK_SIZE); + + /* To be pedantically correct, we would check if the tarball + * is smaller than 20 tar blocks, and pad it if it was smaller, + * but that isn't necessary for GNU tar interoperability, and + * so is considered a waste of space */ + + /* Close so the child process (if any) will exit */ + close(tbInfo->tarFd); + + /* Hang up the tools, close up shop, head home */ + if (ENABLE_FEATURE_CLEAN_UP) + freeHardLinkInfo(&tbInfo->hlInfoHead); + + if (errorFlag) + bb_error_msg("error exit delayed from previous errors"); + +# if SEAMLESS_COMPRESSION + if (gzip) { + int status; + if (safe_waitpid(-1, &status, 0) == -1) + bb_perror_msg("waitpid"); + else if (!WIFEXITED(status) || WEXITSTATUS(status)) + /* gzip was killed or has exited with nonzero! */ + errorFlag = TRUE; + } +# endif + return errorFlag; +} + +#else /* !FEATURE_TAR_CREATE */ + +# define writeTarFile(...) 0 + +#endif + +#if ENABLE_FEATURE_TAR_FROM +static llist_t *append_file_list_to_list(llist_t *list) +{ + llist_t *newlist = NULL; + + while (list) { + FILE *src_stream; + char *line; + + src_stream = xfopen_stdin(llist_pop(&list)); + while ((line = xmalloc_fgetline(src_stream)) != NULL) { + /* kill trailing '/' unless the string is just "/" */ + char *cp = last_char_is(line, '/'); + if (cp > line) + *cp = '\0'; + llist_add_to_end(&newlist, line); + } + fclose(src_stream); + } + return newlist; +} +#endif + +//usage:#define tar_trivial_usage +//usage: IF_FEATURE_TAR_CREATE("c|") "x|t [-" +//usage: IF_FEATURE_SEAMLESS_Z("Z") +//usage: IF_FEATURE_SEAMLESS_GZ("z") +//usage: IF_FEATURE_SEAMLESS_XZ("J") +//usage: IF_FEATURE_SEAMLESS_BZ2("j") +//usage: IF_FEATURE_SEAMLESS_LZMA("a") +//usage: IF_FEATURE_TAR_CREATE("h") +//usage: IF_FEATURE_TAR_NOPRESERVE_TIME("m") +//usage: "vokO] " +//usage: "[-f TARFILE] [-C DIR] " +//usage: IF_FEATURE_TAR_FROM("[-T FILE] [-X FILE] "IF_FEATURE_TAR_LONG_OPTIONS("[--exclude PATTERN]... ")) +//usage: "[FILE]..." +//usage:#define tar_full_usage "\n\n" +//usage: IF_FEATURE_TAR_CREATE("Create, extract, ") +//usage: IF_NOT_FEATURE_TAR_CREATE("Extract ") +//usage: "or list files from a tar file" +//usage: "\n" +//usage: IF_FEATURE_TAR_CREATE( +//usage: "\n c Create" +//usage: ) +//usage: "\n x Extract" +//usage: "\n t List" +//usage: "\n -f FILE Name of TARFILE ('-' for stdin/out)" +//usage: "\n -C DIR Change to DIR before operation" +//usage: "\n -v Verbose" +//usage: "\n -O Extract to stdout" +//usage: IF_FEATURE_TAR_NOPRESERVE_TIME( +//usage: "\n -m Don't restore mtime" +//usage: ) +//usage: "\n -o Don't restore user:group" +///////:-p - accepted but ignored, restores mode (aliases in GNU tar: --preserve-permissions, --same-permissions) +//usage: "\n -k Don't replace existing files" +//usage: IF_FEATURE_SEAMLESS_Z( +//usage: "\n -Z (De)compress using compress" +//usage: ) +//usage: IF_FEATURE_SEAMLESS_GZ( +//usage: "\n -z (De)compress using gzip" +//usage: ) +//usage: IF_FEATURE_SEAMLESS_XZ( +//usage: "\n -J (De)compress using xz" +//usage: ) +//usage: IF_FEATURE_SEAMLESS_BZ2( +//usage: "\n -j (De)compress using bzip2" +//usage: ) +//usage: IF_FEATURE_SEAMLESS_LZMA( +//usage: "\n -a (De)compress using lzma" +//usage: ) +//usage: IF_FEATURE_TAR_CREATE( +//usage: "\n -h Follow symlinks" +//usage: ) +//usage: IF_FEATURE_TAR_FROM( +//usage: "\n -T FILE File with names to include" +//usage: "\n -X FILE File with glob patterns to exclude" +//usage: IF_FEATURE_TAR_LONG_OPTIONS( +//usage: "\n --exclude PATTERN Glob pattern to exclude" +//usage: ) +//usage: ) +//usage: +//usage:#define tar_example_usage +//usage: "$ zcat /tmp/tarball.tar.gz | tar -xf -\n" +//usage: "$ tar -cf /tmp/tarball.tar /usr/local\n" + +// Supported but aren't in --help: +// no-recursion +// numeric-owner +// no-same-permissions +// overwrite +//IF_FEATURE_TAR_TO_COMMAND( +// to-command +//) + +enum { + OPTBIT_KEEP_OLD = 8, + IF_FEATURE_TAR_CREATE( OPTBIT_CREATE ,) + IF_FEATURE_TAR_CREATE( OPTBIT_DEREFERENCE ,) + IF_FEATURE_SEAMLESS_BZ2( OPTBIT_BZIP2 ,) + IF_FEATURE_SEAMLESS_LZMA(OPTBIT_LZMA ,) + IF_FEATURE_TAR_FROM( OPTBIT_INCLUDE_FROM,) + IF_FEATURE_TAR_FROM( OPTBIT_EXCLUDE_FROM,) + IF_FEATURE_SEAMLESS_GZ( OPTBIT_GZIP ,) + IF_FEATURE_SEAMLESS_XZ( OPTBIT_XZ ,) // 16th bit + 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, + OPTBIT_NOPRESERVE_PERM, + OPTBIT_OVERWRITE, +#endif + OPT_TEST = 1 << 0, // t + OPT_EXTRACT = 1 << 1, // x + OPT_BASEDIR = 1 << 2, // C + OPT_TARNAME = 1 << 3, // f + OPT_2STDOUT = 1 << 4, // O + OPT_NOPRESERVE_OWNER = 1 << 5, // o == no-same-owner + OPT_P = 1 << 6, // p + OPT_VERBOSE = 1 << 7, // v + OPT_KEEP_OLD = 1 << 8, // k + OPT_CREATE = IF_FEATURE_TAR_CREATE( (1 << OPTBIT_CREATE )) + 0, // c + OPT_DEREFERENCE = IF_FEATURE_TAR_CREATE( (1 << OPTBIT_DEREFERENCE )) + 0, // h + OPT_BZIP2 = IF_FEATURE_SEAMLESS_BZ2( (1 << OPTBIT_BZIP2 )) + 0, // j + OPT_LZMA = IF_FEATURE_SEAMLESS_LZMA((1 << OPTBIT_LZMA )) + 0, // a + OPT_INCLUDE_FROM = IF_FEATURE_TAR_FROM( (1 << OPTBIT_INCLUDE_FROM)) + 0, // T + OPT_EXCLUDE_FROM = IF_FEATURE_TAR_FROM( (1 << OPTBIT_EXCLUDE_FROM)) + 0, // X + 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_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), +}; +#if ENABLE_FEATURE_TAR_LONG_OPTIONS +static const char tar_longopts[] ALIGN1 = + "list\0" No_argument "t" + "extract\0" No_argument "x" + "directory\0" Required_argument "C" + "file\0" Required_argument "f" + "to-stdout\0" No_argument "O" + /* do not restore owner */ + /* Note: GNU tar handles 'o' as no-same-owner only on extract, + * on create, 'o' is --old-archive. We do not support --old-archive. */ + "no-same-owner\0" No_argument "o" + "same-permissions\0" No_argument "p" + "verbose\0" No_argument "v" + "keep-old\0" No_argument "k" +# if ENABLE_FEATURE_TAR_CREATE + "create\0" No_argument "c" + "dereference\0" No_argument "h" +# endif +# if ENABLE_FEATURE_SEAMLESS_BZ2 + "bzip2\0" No_argument "j" +# endif +# if ENABLE_FEATURE_SEAMLESS_LZMA + "lzma\0" No_argument "a" +# endif +# if ENABLE_FEATURE_TAR_FROM + "files-from\0" Required_argument "T" + "exclude-from\0" Required_argument "X" +# endif +# if ENABLE_FEATURE_SEAMLESS_GZ + "gzip\0" No_argument "z" +# endif +# if ENABLE_FEATURE_SEAMLESS_XZ + "xz\0" No_argument "J" +# endif +# if ENABLE_FEATURE_SEAMLESS_Z + "compress\0" No_argument "Z" +# endif +# 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" +# endif + /* use numeric uid/gid from tar header, not textual */ + "numeric-owner\0" No_argument "\xfc" + /* do not restore mode */ + "no-same-permissions\0" No_argument "\xfd" + /* on unpack, open with O_TRUNC and !O_EXCL */ + "overwrite\0" No_argument "\xfe" + /* --exclude takes next bit position in option mask, */ + /* therefore we have to put it _after_ --no-same-permissions */ +# if ENABLE_FEATURE_TAR_FROM + "exclude\0" Required_argument "\xff" +# endif + ; +# define GETOPT32 getopt32long +# define LONGOPTS ,tar_longopts +#else +# define GETOPT32 getopt32 +# define LONGOPTS +#endif + +int tar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tar_main(int argc UNUSED_PARAM, char **argv) +{ + archive_handle_t *tar_handle; + char *base_dir = NULL; + const char *tar_filename = "-"; + unsigned opt; + int verboseFlag = 0; +#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM + llist_t *excludes = NULL; +#endif + INIT_G(); + + /* Initialise default values */ + tar_handle = init_handle(); + tar_handle->ah_flags = ARCHIVE_CREATE_LEADING_DIRS + | ARCHIVE_RESTORE_DATE + | ARCHIVE_UNLINK_OLD; + + /* Apparently only root's tar preserves perms (see bug 3844) */ + if (getuid() != 0) + tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM; + +#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; + } +#endif + if (argv[1] && argv[1][0] != '-' && argv[1][0] != '\0') { + /* Compat: + * 1st argument without dash handles options with parameters + * differently from dashed one: it takes *next argv[i]* + * as parameter even if there are more chars in 1st argument: + * "tar fx TARFILE" - "x" is not taken as f's param + * but is interpreted as -x option + * "tar -xf TARFILE" - dashed equivalent of the above + * "tar -fx ..." - "x" is taken as f's param + * getopt32 wouldn't handle 1st command correctly. + * Unfortunately, people do use such commands. + * We massage argv[1] to work around it by moving 'f' + * to the end of the string. + * More contrived "tar fCx TARFILE DIR" still fails, + * but such commands are much less likely to be used. + */ + char *f = strchr(argv[1], 'f'); + if (f) { + while (f[1] != '\0') { + *f = f[1]; + f++; + } + *f = 'f'; + } + /* Prepend '-' to the first argument */ + argv[1] = xasprintf("-%s", argv[1]); + } + 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_NOPRESERVE_TIME("m") + IF_FEATURE_TAR_LONG_OPTIONS("\xf9:") // --strip-components + "\0" + "tt:vv:" // count -t,-v +#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM + "\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 ENABLE_FEATURE_TAR_LONG_OPTIONS + ":\xf9+" // --strip-components=NUM +#endif + LONGOPTS + , &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 +#endif + , &verboseFlag // combined count for -t and -v + , &verboseFlag // combined count for -t and -v + ); +#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 (opt & OPT_EXTRACT) + tar_handle->action_data = data_extract_all; + + if (opt & OPT_2STDOUT) + tar_handle->action_data = data_extract_to_stdout; + + if (opt & OPT_2COMMAND) { + putenv((char*)"TAR_FILETYPE=f"); + signal(SIGPIPE, SIG_IGN); + tar_handle->action_data = data_extract_to_command; + IF_FEATURE_TAR_TO_COMMAND(tar_handle->tar__to_command_shell = xstrdup(get_shell_name());) + } + + if (opt & OPT_KEEP_OLD) + tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD; + + if (opt & OPT_NUMERIC_OWNER) + tar_handle->ah_flags |= ARCHIVE_NUMERIC_OWNER; + + if (opt & OPT_NOPRESERVE_OWNER) + tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_OWNER; + + if (opt & OPT_NOPRESERVE_PERM) + tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM; + + if (opt & OPT_OVERWRITE) { + tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD; + tar_handle->ah_flags |= ARCHIVE_O_TRUNC; + } + + if (opt & OPT_NOPRESERVE_TIME) + tar_handle->ah_flags &= ~ARCHIVE_RESTORE_DATE; + +#if ENABLE_FEATURE_TAR_FROM + tar_handle->reject = append_file_list_to_list(tar_handle->reject); +# if ENABLE_FEATURE_TAR_LONG_OPTIONS + /* Append excludes to reject */ + while (excludes) { + llist_t *next = excludes->link; + excludes->link = tar_handle->reject; + tar_handle->reject = excludes; + excludes = next; + } +# endif + tar_handle->accept = append_file_list_to_list(tar_handle->accept); +#endif + + /* Setup an array of filenames to work with */ + /* TODO: This is the same as in ar, make a separate function? */ + while (*argv) { + /* kill trailing '/' unless the string is just "/" */ + char *cp = last_char_is(*argv, '/'); + if (cp > *argv) + *cp = '\0'; + llist_add_to_end(&tar_handle->accept, *argv); + argv++; + } + + if (tar_handle->accept || tar_handle->reject) + tar_handle->filter = filter_accept_reject_list; + + /* Open the tar file */ + { + int tar_fd = STDIN_FILENO; + int flags = O_RDONLY; + + if (opt & OPT_CREATE) { + /* Make sure there is at least one file to tar up */ + if (tar_handle->accept == NULL) + bb_error_msg_and_die("empty archive"); + + tar_fd = STDOUT_FILENO; + /* Mimicking GNU tar 1.15.1: */ + flags = O_WRONLY | O_CREAT | O_TRUNC; + } + + if (LONE_DASH(tar_filename)) { + tar_handle->src_fd = tar_fd; + tar_handle->seek = seek_by_read; + } else { + if (ENABLE_FEATURE_TAR_AUTODETECT + && flags == O_RDONLY + && !(opt & OPT_ANY_COMPRESS) + ) { + tar_handle->src_fd = open_zipped(tar_filename, /*fail_if_not_compressed:*/ 0); + if (tar_handle->src_fd < 0) + bb_perror_msg_and_die("can't open '%s'", tar_filename); + } else { + tar_handle->src_fd = xopen(tar_filename, flags); + } + } + } + + if (base_dir) + xchdir(base_dir); + +#if ENABLE_FEATURE_TAR_CREATE + /* Create an archive */ + if (opt & OPT_CREATE) { + struct TarBallInfo *tbInfo; +# 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 + tbInfo = xzalloc(sizeof(*tbInfo)); + tbInfo->tarFd = tar_handle->src_fd; + tbInfo->verboseFlag = verboseFlag; +# if ENABLE_FEATURE_TAR_FROM + tbInfo->excludeList = tar_handle->reject; +# endif + /* NB: writeTarFile() closes tar_handle->src_fd */ + return writeTarFile(tbInfo, + (opt & OPT_DEREFERENCE ? ACTION_FOLLOWLINKS : 0) + | (opt & OPT_NORECURSION ? 0 : ACTION_RECURSE), + tar_handle->accept, + zipMode); + } +#endif + + if (opt & OPT_ANY_COMPRESS) { + 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) { + USE_FOR_MMU(IF_FEATURE_SEAMLESS_Z(xformer = unpack_Z_stream;)) + USE_FOR_NOMMU(xformer_prog = "uncompress";) + } + if (opt & OPT_GZIP) { + USE_FOR_MMU(IF_FEATURE_SEAMLESS_GZ(xformer = unpack_gz_stream;)) + USE_FOR_NOMMU(xformer_prog = "gunzip";) + } + if (opt & OPT_BZIP2) { + USE_FOR_MMU(IF_FEATURE_SEAMLESS_BZ2(xformer = unpack_bz2_stream;)) + USE_FOR_NOMMU(xformer_prog = "bunzip2";) + } + if (opt & OPT_LZMA) { + USE_FOR_MMU(IF_FEATURE_SEAMLESS_LZMA(xformer = unpack_lzma_stream;)) + USE_FOR_NOMMU(xformer_prog = "unlzma";) + } + if (opt & OPT_XZ) { + USE_FOR_MMU(IF_FEATURE_SEAMLESS_XZ(xformer = unpack_xz_stream;)) + USE_FOR_NOMMU(xformer_prog = "unxz";) + } + + 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 */ + } + + /* Zero processed headers (== empty file) is not a valid tarball. + * We (ab)use bb_got_signal as exitcode here, + * because check_errors_in_children() uses _it_ as error indicator. + */ + bb_got_signal = EXIT_FAILURE; + + while (get_header_tar(tar_handle) == EXIT_SUCCESS) + bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */ + + create_links_from_list(tar_handle->link_placeholders); + + /* Check that every file that should have been extracted was */ + while (tar_handle->accept) { + if (!find_list_entry(tar_handle->reject, tar_handle->accept->data) + && !find_list_entry(tar_handle->passed, tar_handle->accept->data) + ) { + bb_error_msg_and_die("%s: not found in archive", + tar_handle->accept->data); + } + tar_handle->accept = tar_handle->accept->link; + } + if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */) + close(tar_handle->src_fd); + + if (SEAMLESS_COMPRESSION || OPT_COMPRESS) { + /* Set bb_got_signal to 1 if a child died with !0 exitcode */ + check_errors_in_children(0); + } + + return bb_got_signal; +} diff --git a/busybox/archival/tar_symlink_attack b/busybox/archival/tar_symlink_attack new file mode 100644 index 00000000000000..35455f2000cff3 --- /dev/null +++ b/busybox/archival/tar_symlink_attack @@ -0,0 +1,16 @@ +#!/bin/sh +# Makes "symlink attack" tarball (needs GNU tar for --append) + +true >anything.txt +tar cvf tar_symlink_attack.tar anything.txt +rm anything.txt + +ln -s /tmp symlink +tar --append -f tar_symlink_attack.tar symlink +rm symlink + +mkdir symlink +echo BUG >symlink/bb_test_evilfile +tar --append -f tar_symlink_attack.tar symlink/bb_test_evilfile +rm symlink/bb_test_evilfile +rmdir symlink diff --git a/busybox/archival/unzip.c b/busybox/archival/unzip.c new file mode 100644 index 00000000000000..96b7ab69b403b2 --- /dev/null +++ b/busybox/archival/unzip.c @@ -0,0 +1,1023 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini unzip implementation for busybox + * + * Copyright (C) 2004 by Ed Clark + * + * Loosely based on original busybox unzip applet by Laurence Anderson. + * All options and features should work in this version. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* For reference see + * http://www.pkware.com/company/standards/appnote/ + * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip + * + * TODO + * Zip64 + other methods + */ +//config:config UNZIP +//config: bool "unzip (24 kb)" +//config: default y +//config: help +//config: unzip will list or extract files from a ZIP archive, +//config: commonly found on DOS/WIN systems. The default behavior +//config: (with no options) is to extract the archive into the +//config: current directory. +//config: +//config:config FEATURE_UNZIP_CDF +//config: bool "Read and use Central Directory data" +//config: default y +//config: depends on UNZIP +//config: help +//config: If you know that you only need to deal with simple +//config: ZIP files without deleted/updated files, SFX archives etc, +//config: you can reduce code size by unselecting this option. +//config: To support less trivial ZIPs, say Y. +//config: +//config:config FEATURE_UNZIP_BZIP2 +//config: bool "Support compression method 12 (bzip2)" +//config: default y +//config: depends on FEATURE_UNZIP_CDF && DESKTOP +// FEATURE_UNZIP_CDF is needed, otherwise we can't find start of next file +// DESKTOP is needed to get back uncompressed length +//config: +//config:config FEATURE_UNZIP_LZMA +//config: bool "Support compression method 14 (lzma)" +//config: default y +//config: depends on FEATURE_UNZIP_CDF && DESKTOP +//config: +//config:config FEATURE_UNZIP_XZ +//config: bool "Support compression method 95 (xz)" +//config: default y +//config: depends on FEATURE_UNZIP_CDF && DESKTOP + +//applet:IF_UNZIP(APPLET(unzip, BB_DIR_USR_BIN, BB_SUID_DROP)) +//kbuild:lib-$(CONFIG_UNZIP) += unzip.o + +//usage:#define unzip_trivial_usage +//usage: "[-lnojpq] FILE[.zip] [FILE]... [-x FILE...] [-d DIR]" +//usage:#define unzip_full_usage "\n\n" +//usage: "Extract FILEs from ZIP archive\n" +//usage: "\n -l List contents (with -q for short form)" +//usage: "\n -n Never overwrite files (default: ask)" +//usage: "\n -o Overwrite" +//usage: "\n -j Do not restore paths" +//usage: "\n -p Print to stdout" +//usage: "\n -q Quiet" +//usage: "\n -x FILE Exclude FILEs" +//usage: "\n -d DIR Extract into DIR" + +#include "libbb.h" +#include "bb_archive.h" + +#if 0 +# define dbg(...) bb_error_msg(__VA_ARGS__) +#else +# define dbg(...) ((void)0) +#endif + +enum { +#if BB_BIG_ENDIAN + ZIP_FILEHEADER_MAGIC = 0x504b0304, + ZIP_CDF_MAGIC = 0x504b0102, /* CDF item */ + ZIP_CDE_MAGIC = 0x504b0506, /* End of CDF */ + ZIP_DD_MAGIC = 0x504b0708, +#else + ZIP_FILEHEADER_MAGIC = 0x04034b50, + ZIP_CDF_MAGIC = 0x02014b50, + ZIP_CDE_MAGIC = 0x06054b50, + ZIP_DD_MAGIC = 0x08074b50, +#endif +}; + +#define ZIP_HEADER_LEN 26 + +typedef union { + uint8_t raw[ZIP_HEADER_LEN]; + struct { + uint16_t version; /* 0-1 */ + uint16_t zip_flags; /* 2-3 */ + uint16_t method; /* 4-5 */ + uint16_t modtime; /* 6-7 */ + uint16_t moddate; /* 8-9 */ + uint32_t crc32 PACKED; /* 10-13 */ + uint32_t cmpsize PACKED; /* 14-17 */ + uint32_t ucmpsize PACKED; /* 18-21 */ + uint16_t filename_len; /* 22-23 */ + uint16_t extra_len; /* 24-25 */ + /* filename follows (not NUL terminated) */ + /* extra field follows */ + /* data follows */ + } fmt PACKED; +} zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */ + +#define FIX_ENDIANNESS_ZIP(zip) \ +do { if (BB_BIG_ENDIAN) { \ + (zip).fmt.method = SWAP_LE16((zip).fmt.method ); \ + (zip).fmt.crc32 = SWAP_LE32((zip).fmt.crc32 ); \ + (zip).fmt.cmpsize = SWAP_LE32((zip).fmt.cmpsize ); \ + (zip).fmt.ucmpsize = SWAP_LE32((zip).fmt.ucmpsize ); \ + (zip).fmt.filename_len = SWAP_LE16((zip).fmt.filename_len); \ + (zip).fmt.extra_len = SWAP_LE16((zip).fmt.extra_len ); \ +}} while (0) + +#define CDF_HEADER_LEN 42 + +typedef union { + uint8_t raw[CDF_HEADER_LEN]; + struct { + /* uint32_t signature; 50 4b 01 02 */ + uint16_t version_made_by; /* 0-1 */ + uint16_t version_needed; /* 2-3 */ + uint16_t cdf_flags; /* 4-5 */ + uint16_t method; /* 6-7 */ + uint16_t modtime; /* 8-9 */ + uint16_t moddate; /* 10-11 */ + uint32_t crc32; /* 12-15 */ + uint32_t cmpsize; /* 16-19 */ + uint32_t ucmpsize; /* 20-23 */ + uint16_t filename_len; /* 24-25 */ + uint16_t extra_len; /* 26-27 */ + uint16_t file_comment_length; /* 28-29 */ + uint16_t disk_number_start; /* 30-31 */ + uint16_t internal_attributes; /* 32-33 */ + uint32_t external_attributes PACKED; /* 34-37 */ + uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ + /* filename follows (not NUL terminated) */ + /* extra field follows */ + /* file comment follows */ + } fmt PACKED; +} cdf_header_t; + +#define FIX_ENDIANNESS_CDF(cdf) \ +do { if (BB_BIG_ENDIAN) { \ + (cdf).fmt.version_made_by = SWAP_LE16((cdf).fmt.version_made_by); \ + (cdf).fmt.version_needed = SWAP_LE16((cdf).fmt.version_needed); \ + (cdf).fmt.method = SWAP_LE16((cdf).fmt.method ); \ + (cdf).fmt.modtime = SWAP_LE16((cdf).fmt.modtime ); \ + (cdf).fmt.moddate = SWAP_LE16((cdf).fmt.moddate ); \ + (cdf).fmt.crc32 = SWAP_LE32((cdf).fmt.crc32 ); \ + (cdf).fmt.cmpsize = SWAP_LE32((cdf).fmt.cmpsize ); \ + (cdf).fmt.ucmpsize = SWAP_LE32((cdf).fmt.ucmpsize ); \ + (cdf).fmt.filename_len = SWAP_LE16((cdf).fmt.filename_len); \ + (cdf).fmt.extra_len = SWAP_LE16((cdf).fmt.extra_len ); \ + (cdf).fmt.file_comment_length = SWAP_LE16((cdf).fmt.file_comment_length); \ + (cdf).fmt.external_attributes = SWAP_LE32((cdf).fmt.external_attributes); \ +}} while (0) + +#define CDE_LEN 16 + +typedef union { + uint8_t raw[CDE_LEN]; + struct { + /* uint32_t signature; 50 4b 05 06 */ + uint16_t this_disk_no; + uint16_t disk_with_cdf_no; + uint16_t cdf_entries_on_this_disk; + uint16_t cdf_entries_total; + uint32_t cdf_size; + uint32_t cdf_offset; + /* uint16_t archive_comment_length; */ + /* archive comment follows */ + } fmt PACKED; +} cde_t; + +#define FIX_ENDIANNESS_CDE(cde) \ +do { if (BB_BIG_ENDIAN) { \ + (cde).fmt.cdf_offset = SWAP_LE32((cde).fmt.cdf_offset); \ +}} while (0) + +struct BUG { + /* Check the offset of the last element, not the length. This leniency + * allows for poor packing, whereby the overall struct may be too long, + * even though the elements are all in the right place. + */ + char BUG_zip_header_must_be_26_bytes[ + offsetof(zip_header_t, fmt.extra_len) + 2 + == ZIP_HEADER_LEN ? 1 : -1]; + char BUG_cdf_header_must_be_42_bytes[ + offsetof(cdf_header_t, fmt.relative_offset_of_local_header) + 4 + == CDF_HEADER_LEN ? 1 : -1]; + char BUG_cde_must_be_16_bytes[ + sizeof(cde_t) == CDE_LEN ? 1 : -1]; +}; + + +enum { zip_fd = 3 }; + + +/* This value means that we failed to find CDF */ +#define BAD_CDF_OFFSET ((uint32_t)0xffffffff) + +#if !ENABLE_FEATURE_UNZIP_CDF + +# define find_cdf_offset() BAD_CDF_OFFSET + +#else +/* Seen in the wild: + * Self-extracting PRO2K3XP_32.exe contains 19078464 byte zip archive, + * where CDE was nearly 48 kbytes before EOF. + * (Surprisingly, it also apparently has *another* CDE structure + * closer to the end, with bogus cdf_offset). + * To make extraction work, bumped PEEK_FROM_END from 16k to 64k. + */ +#define PEEK_FROM_END (64*1024) +/* NB: does not preserve file position! */ +static uint32_t find_cdf_offset(void) +{ + cde_t cde; + unsigned char *buf; + unsigned char *p; + off_t end; + uint32_t found; + + end = lseek(zip_fd, 0, SEEK_END); + if (end == (off_t) -1) + return BAD_CDF_OFFSET; + + end -= PEEK_FROM_END; + if (end < 0) + end = 0; + + dbg("Looking for cdf_offset starting from 0x%"OFF_FMT"x", end); + xlseek(zip_fd, end, SEEK_SET); + buf = xzalloc(PEEK_FROM_END); + full_read(zip_fd, buf, PEEK_FROM_END); + + found = BAD_CDF_OFFSET; + p = buf; + while (p <= buf + PEEK_FROM_END - CDE_LEN - 4) { + if (*p != 'P') { + p++; + continue; + } + if (*++p != 'K') + continue; + if (*++p != 5) + continue; + if (*++p != 6) + continue; + /* we found CDE! */ + memcpy(cde.raw, p + 1, CDE_LEN); + FIX_ENDIANNESS_CDE(cde); + /* + * I've seen .ZIP files with seemingly valid CDEs + * where cdf_offset points past EOF - ?? + * This check ignores such CDEs: + */ + if (cde.fmt.cdf_offset < end + (p - buf)) { + found = cde.fmt.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.fmt.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); + dbg("Found cdf_offset:0x%x", (unsigned)found); + return found; +}; + +static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf) +{ + uint32_t magic; + + if (cdf_offset == BAD_CDF_OFFSET) + return cdf_offset; + + dbg("Reading CDF at 0x%x", (unsigned)cdf_offset); + xlseek(zip_fd, cdf_offset, SEEK_SET); + xread(zip_fd, &magic, 4); + /* Central Directory End? Assume CDF has ended. + * (more correct method is to use cde.cdf_entries_total counter) + */ + if (magic == ZIP_CDE_MAGIC) { + dbg("got ZIP_CDE_MAGIC"); + return 0; /* EOF */ + } + xread(zip_fd, cdf->raw, CDF_HEADER_LEN); + + FIX_ENDIANNESS_CDF(*cdf); + dbg(" filename_len:%u extra_len:%u file_comment_length:%u", + (unsigned)cdf->fmt.filename_len, + (unsigned)cdf->fmt.extra_len, + (unsigned)cdf->fmt.file_comment_length + ); + cdf_offset += 4 + CDF_HEADER_LEN + + cdf->fmt.filename_len + + cdf->fmt.extra_len + + cdf->fmt.file_comment_length; + + return cdf_offset; +}; +#endif + +static void die_if_bad_fnamesize(unsigned sz) +{ + if (sz > 0xfff) /* more than 4k?! no funny business please */ + bb_error_msg_and_die("bad archive"); +} + +static void unzip_skip(off_t skip) +{ + if (skip != 0) + if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) + bb_copyfd_exact_size(zip_fd, -1, skip); +} + +static void unzip_create_leading_dirs(const char *fn) +{ + /* Create all leading directories */ + char *name = xstrdup(fn); + + /* mode of -1: set mode according to umask */ + if (bb_make_directory(dirname(name), -1, FILEUTILS_RECUR)) { + xfunc_die(); /* bb_make_directory is noisy */ + } + free(name); +} + +#if ENABLE_FEATURE_UNZIP_CDF +static void unzip_extract_symlink(llist_t **symlink_placeholders, + zip_header_t *zip, + const char *dst_fn) +{ + char *target; + + die_if_bad_fnamesize(zip->fmt.ucmpsize); + + if (zip->fmt.method == 0) { + /* Method 0 - stored (not compressed) */ + target = xzalloc(zip->fmt.ucmpsize + 1); + xread(zip_fd, target, zip->fmt.ucmpsize); + } else { +#if 1 + bb_error_msg_and_die("compressed symlink is not supported"); +#else + transformer_state_t xstate; + init_transformer_state(&xstate); + xstate.mem_output_size_max = zip->fmt.ucmpsize; + /* ...unpack... */ + if (!xstate.mem_output_buf) + WTF(); + target = xstate.mem_output_buf; + target = xrealloc(target, xstate.mem_output_size + 1); + target[xstate.mem_output_size] = '\0'; +#endif + } + create_or_remember_link(symlink_placeholders, + target, + dst_fn, + 0); + free(target); +} +#endif + +static void unzip_extract(zip_header_t *zip, int dst_fd) +{ + transformer_state_t xstate; + + if (zip->fmt.method == 0) { + /* Method 0 - stored (not compressed) */ + off_t size = zip->fmt.ucmpsize; + if (size) + bb_copyfd_exact_size(zip_fd, dst_fd, size); + return; + } + + init_transformer_state(&xstate); + xstate.bytes_in = zip->fmt.cmpsize; + xstate.src_fd = zip_fd; + xstate.dst_fd = dst_fd; + if (zip->fmt.method == 8) { + /* Method 8 - inflate */ + if (inflate_unzip(&xstate) < 0) + bb_error_msg_and_die("inflate error"); + /* Validate decompression - crc */ + if (zip->fmt.crc32 != (xstate.crc32 ^ 0xffffffffL)) { + bb_error_msg_and_die("crc error"); + } + } +#if ENABLE_FEATURE_UNZIP_BZIP2 + else if (zip->fmt.method == 12) { + /* Tested. Unpacker reads too much, but we use CDF + * and will seek to the correct beginning of next file. + */ + xstate.bytes_out = unpack_bz2_stream(&xstate); + if (xstate.bytes_out < 0) + bb_error_msg_and_die("inflate error"); + } +#endif +#if ENABLE_FEATURE_UNZIP_LZMA + else if (zip->fmt.method == 14) { + /* Not tested yet */ + xstate.bytes_out = unpack_lzma_stream(&xstate); + if (xstate.bytes_out < 0) + bb_error_msg_and_die("inflate error"); + } +#endif +#if ENABLE_FEATURE_UNZIP_XZ + else if (zip->fmt.method == 95) { + /* Not tested yet */ + xstate.bytes_out = unpack_xz_stream(&xstate); + if (xstate.bytes_out < 0) + bb_error_msg_and_die("inflate error"); + } +#endif + else { + bb_error_msg_and_die("unsupported method %u", zip->fmt.method); + } + + /* Validate decompression - size */ + if (zip->fmt.ucmpsize != xstate.bytes_out) { + /* Don't die. Who knows, maybe len calculation + * was botched somewhere. After all, crc matched! */ + bb_error_msg("bad length"); + } +} + +static void my_fgets80(char *buf80) +{ + fflush_all(); + if (!fgets(buf80, 80, stdin)) { + bb_perror_msg_and_die("can't read standard input"); + } +} + +static int get_lstat_mode(const char *dst_fn) +{ + struct stat stat_buf; + if (lstat(dst_fn, &stat_buf) == -1) { + if (errno != ENOENT) { + bb_perror_msg_and_die("can't stat '%s'", dst_fn); + } + /* File does not exist */ + return -1; + } + return stat_buf.st_mode; +} + +int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int unzip_main(int argc, char **argv) +{ + enum { + OPT_l = (1 << 0), + OPT_x = (1 << 1), + OPT_j = (1 << 2), + }; + unsigned opts; + smallint quiet = 0; + IF_NOT_FEATURE_UNZIP_CDF(const) smallint verbose = 0; + enum { O_PROMPT, O_NEVER, O_ALWAYS }; + smallint overwrite = O_PROMPT; + uint32_t cdf_offset; + unsigned long total_usize; + unsigned long total_size; + unsigned total_entries; + int dst_fd = -1; + char *src_fn = NULL; + char *dst_fn = NULL; + llist_t *zaccept = NULL; + llist_t *zreject = NULL; + char *base_dir = NULL; +#if ENABLE_FEATURE_UNZIP_CDF + llist_t *symlink_placeholders = NULL; +#endif + int i; + char key_buf[80]; /* must match size used by my_fgets80 */ + +/* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP: + * + * # /usr/bin/unzip -qq -v decompress_unlzma.i.zip + * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i + * # /usr/bin/unzip -q -v decompress_unlzma.i.zip + * Length Method Size Ratio Date Time CRC-32 Name + * -------- ------ ------- ----- ---- ---- ------ ---- + * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i + * -------- ------- --- ------- + * 204372 35278 83% 1 file + * # /usr/bin/unzip -v decompress_unlzma.i.zip + * Archive: decompress_unlzma.i.zip + * Length Method Size Ratio Date Time CRC-32 Name + * -------- ------ ------- ----- ---- ---- ------ ---- + * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i + * -------- ------- --- ------- + * 204372 35278 83% 1 file + * # unzip -v decompress_unlzma.i.zip + * Archive: decompress_unlzma.i.zip + * Length Date Time Name + * -------- ---- ---- ---- + * 204372 09-06-09 14:23 decompress_unlzma.i + * -------- ------- + * 204372 1 files + * # /usr/bin/unzip -l -qq decompress_unlzma.i.zip + * 204372 09-06-09 14:23 decompress_unlzma.i + * # /usr/bin/unzip -l -q decompress_unlzma.i.zip + * Length Date Time Name + * -------- ---- ---- ---- + * 204372 09-06-09 14:23 decompress_unlzma.i + * -------- ------- + * 204372 1 file + * # /usr/bin/unzip -l decompress_unlzma.i.zip + * Archive: decompress_unlzma.i.zip + * Length Date Time Name + * -------- ---- ---- ---- + * 204372 09-06-09 14:23 decompress_unlzma.i + * -------- ------- + * 204372 1 file + */ + + opts = 0; + /* '-' makes getopt return 1 for non-options */ + while ((i = getopt(argc, argv, "-d:lnopqxjv")) != -1) { + switch (i) { + case 'd': /* Extract to base directory */ + base_dir = optarg; + break; + + case 'l': /* List */ + opts |= OPT_l; + break; + + case 'n': /* Never overwrite existing files */ + overwrite = O_NEVER; + break; + + case 'o': /* Always overwrite existing files */ + overwrite = O_ALWAYS; + break; + + case 'p': /* Extract files to stdout and fall through to set verbosity */ + dst_fd = STDOUT_FILENO; + + case 'q': /* Be quiet */ + quiet++; + break; + + case 'v': /* Verbose list */ + IF_FEATURE_UNZIP_CDF(verbose++;) + opts |= OPT_l; + break; + + case 'x': + opts |= OPT_x; + break; + + case 'j': + opts |= OPT_j; + break; + + case 1: + if (!src_fn) { + /* The zip file */ + /* +5: space for ".zip" and NUL */ + src_fn = xmalloc(strlen(optarg) + 5); + strcpy(src_fn, optarg); + } else if (!(opts & OPT_x)) { + /* Include files */ + llist_add_to(&zaccept, optarg); + } else { + /* Exclude files */ + llist_add_to(&zreject, optarg); + } + break; + + default: + bb_show_usage(); + } + } + +#ifndef __GLIBC__ + /* + * This code is needed for non-GNU getopt + * which doesn't understand "-" in option string. + * The -x option won't work properly in this case: + * "unzip a.zip q -x w e" will be interpreted as + * "unzip a.zip q w e -x" = "unzip a.zip q w e" + */ + argv += optind; + if (argv[0]) { + /* +5: space for ".zip" and NUL */ + src_fn = xmalloc(strlen(argv[0]) + 5); + strcpy(src_fn, argv[0]); + while (*++argv) + llist_add_to(&zaccept, *argv); + } +#endif + + if (!src_fn) { + bb_show_usage(); + } + + /* Open input file */ + if (LONE_DASH(src_fn)) { + xdup2(STDIN_FILENO, zip_fd); + /* Cannot use prompt mode since zip data is arriving on STDIN */ + if (overwrite == O_PROMPT) + overwrite = O_NEVER; + } else { + static const char extn[][5] ALIGN1 = { ".zip", ".ZIP" }; + char *ext = src_fn + strlen(src_fn); + int src_fd; + + i = 0; + for (;;) { + src_fd = open(src_fn, O_RDONLY); + if (src_fd >= 0) + break; + if (++i > 2) { + *ext = '\0'; + bb_error_msg_and_die("can't open %s[.zip]", src_fn); + } + strcpy(ext, extn[i - 1]); + } + xmove_fd(src_fd, zip_fd); + } + + /* Change dir if necessary */ + if (base_dir) + xchdir(base_dir); + + if (quiet <= 1) { /* not -qq */ + if (quiet == 0) + printf("Archive: %s\n", src_fn); + if (opts & OPT_l) { + puts(verbose ? + " Length Method Size Cmpr Date Time CRC-32 Name\n" + "-------- ------ ------- ---- ---------- ----- -------- ----" + : + " Length Date Time Name\n" + "--------- ---------- ----- ----" + ); + } + } + +/* Example of an archive with one 0-byte long file named 'z' + * created by Zip 2.31 on Unix: + * 0000 [50 4b]03 04 0a 00 00 00 00 00 42 1a b8 3c 00 00 |PK........B..<..| + * sig........ vneed flags compr mtime mdate crc32> + * 0010 00 00 00 00 00 00 00 00 00 00 01 00 15 00 7a 55 |..............zU| + * >..... csize...... usize...... fnlen exlen fn ex> + * 0020 54 09 00 03 cc d3 f9 4b cc d3 f9 4b 55 78 04 00 |T......K...KUx..| + * >tra_field...................................... + * 0030 00 00 00 00[50 4b]01 02 17 03 0a 00 00 00 00 00 |....PK..........| + * ........... sig........ vmade vneed flags compr + * 0040 42 1a b8 3c 00 00 00 00 00 00 00 00 00 00 00 00 |B..<............| + * mtime mdate crc32...... csize...... usize...... + * 0050 01 00 0d 00 00 00 00 00 00 00 00 00 a4 81 00 00 |................| + * fnlen exlen clen. dnum. iattr eattr...... relofs> (eattr = rw-r--r--) + * 0060 00 00 7a 55 54 05 00 03 cc d3 f9 4b 55 78 00 00 |..zUT......KUx..| + * >..... fn extra_field........................... + * 0070 [50 4b]05 06 00 00 00 00 01 00 01 00 3c 00 00 00 |PK..........<...| + * 0080 34 00 00 00 00 00 |4.....| + */ + total_usize = 0; + total_size = 0; + total_entries = 0; + cdf_offset = find_cdf_offset(); /* try to seek to the end, find CDE and CDF start */ + while (1) { + zip_header_t zip; + mode_t dir_mode = 0777; +#if ENABLE_FEATURE_UNZIP_CDF + mode_t file_mode = 0666; +#endif + + if (!ENABLE_FEATURE_UNZIP_CDF || cdf_offset == BAD_CDF_OFFSET) { + /* Normally happens when input is unseekable. + * + * Valid ZIP file has Central Directory at the end + * with central directory file headers (CDFs). + * After it, there is a Central Directory End structure. + * CDFs identify what files are in the ZIP and where + * they are located. This allows ZIP readers to load + * the list of files without reading the entire ZIP archive. + * ZIP files may be appended to, only files specified in + * the CD are valid. Scanning for local file headers is + * not a correct algorithm. + * + * We try to do the above, and resort to "linear" reading + * of ZIP file only if seek failed or CDE wasn't found. + */ + uint32_t magic; + + /* Check magic number */ + xread(zip_fd, &magic, 4); + /* CDF item? Assume there are no more files, exit */ + if (magic == ZIP_CDF_MAGIC) { + dbg("got ZIP_CDF_MAGIC"); + break; + } + /* 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; + } + if (magic != ZIP_FILEHEADER_MAGIC) + bb_error_msg_and_die("invalid zip magic %08X", (int)magic); + dbg("got ZIP_FILEHEADER_MAGIC"); + + xread(zip_fd, zip.raw, ZIP_HEADER_LEN); + FIX_ENDIANNESS_ZIP(zip); + if (zip.fmt.zip_flags & SWAP_LE16(0x0008)) { + bb_error_msg_and_die("zip flag %s is not supported", + "8 (streaming)"); + } + } +#if ENABLE_FEATURE_UNZIP_CDF + else { + /* cdf_offset is valid (and we know the file is seekable) */ + cdf_header_t cdf; + cdf_offset = read_next_cdf(cdf_offset, &cdf); + if (cdf_offset == 0) /* EOF? */ + break; +# if 1 + xlseek(zip_fd, + SWAP_LE32(cdf.fmt.relative_offset_of_local_header) + 4, + SEEK_SET); + xread(zip_fd, zip.raw, ZIP_HEADER_LEN); + FIX_ENDIANNESS_ZIP(zip); + if (zip.fmt.zip_flags & SWAP_LE16(0x0008)) { + /* 0x0008 - streaming. [u]cmpsize can be reliably gotten + * only from Central Directory. + */ + zip.fmt.crc32 = cdf.fmt.crc32; + zip.fmt.cmpsize = cdf.fmt.cmpsize; + zip.fmt.ucmpsize = cdf.fmt.ucmpsize; + } +// Seen in some zipfiles: central directory 9 byte extra field contains +// a subfield with ID 0x5455 and 5 data bytes, which is a Unix-style UTC mtime. +// Local header version: +// u16 0x5455 ("UT") +// u16 size (1 + 4 * n) +// u8 flags: bit 0:mtime is present, bit 1:atime is present, bit 2:ctime is present +// u32 mtime +// u32 atime +// u32 ctime +// Central header version: +// u16 0x5455 ("UT") +// u16 size (5 (or 1?)) +// u8 flags: bit 0:mtime is present, bit 1:atime is present, bit 2:ctime is present +// u32 mtime (CDF does not store atime/ctime) +# else + /* CDF has the same data as local header, no need to read the latter... + * ...not really. An archive was seen with cdf.extra_len == 6 but + * zip.extra_len == 0. + */ + memcpy(&zip.fmt.version, + &cdf.fmt.version_needed, ZIP_HEADER_LEN); + xlseek(zip_fd, + SWAP_LE32(cdf.fmt.relative_offset_of_local_header) + 4 + ZIP_HEADER_LEN, + SEEK_SET); +# endif + if ((cdf.fmt.version_made_by >> 8) == 3) { + /* This archive is created on Unix */ + dir_mode = file_mode = (cdf.fmt.external_attributes >> 16); + } + } +#endif + + if (zip.fmt.zip_flags & SWAP_LE16(0x0001)) { + /* 0x0001 - encrypted */ + bb_error_msg_and_die("zip flag %s is not supported", + "1 (encryption)"); + } + dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x", + (unsigned)zip.fmt.cmpsize, + (unsigned)zip.fmt.extra_len, + (unsigned)zip.fmt.ucmpsize + ); + + /* Read filename */ + free(dst_fn); + die_if_bad_fnamesize(zip.fmt.filename_len); + dst_fn = xzalloc(zip.fmt.filename_len + 1); + xread(zip_fd, dst_fn, zip.fmt.filename_len); + /* Skip extra header bytes */ + unzip_skip(zip.fmt.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 */ + goto skip_cmpsize; + } + + if (opts & OPT_l) { + /* List entry */ + char dtbuf[sizeof("mm-dd-yyyy hh:mm")]; + sprintf(dtbuf, "%02u-%02u-%04u %02u:%02u", + (zip.fmt.moddate >> 5) & 0xf, // mm: 0x01e0 + (zip.fmt.moddate) & 0x1f, // dd: 0x001f + (zip.fmt.moddate >> 9) + 1980, // yy: 0xfe00 + (zip.fmt.modtime >> 11), // hh: 0xf800 + (zip.fmt.modtime >> 5) & 0x3f // mm: 0x07e0 + // seconds/2 not shown, encoded in -- 0x001f + ); + if (!verbose) { + // " Length Date Time Name\n" + // "--------- ---------- ----- ----" + printf( "%9u " "%s " "%s\n", + (unsigned)zip.fmt.ucmpsize, + dtbuf, + dst_fn); + } else { + char method6[7]; + unsigned long percents; + + sprintf(method6, "%6u", zip.fmt.method); + if (zip.fmt.method == 0) { + strcpy(method6, "Stored"); + } + if (zip.fmt.method == 8) { + strcpy(method6, "Defl:N"); + /* normal, maximum, fast, superfast */ + IF_DESKTOP(method6[5] = "NXFS"[(zip.fmt.zip_flags >> 1) & 3];) + } + percents = zip.fmt.ucmpsize - zip.fmt.cmpsize; + if ((int32_t)percents < 0) + percents = 0; /* happens if ucmpsize < cmpsize */ + percents = percents * 100; + if (zip.fmt.ucmpsize) + percents /= zip.fmt.ucmpsize; + // " Length Method Size Cmpr Date Time CRC-32 Name\n" + // "-------- ------ ------- ---- ---------- ----- -------- ----" + printf( "%8u %s" "%9u%4u%% " "%s " "%08x " "%s\n", + (unsigned)zip.fmt.ucmpsize, + method6, + (unsigned)zip.fmt.cmpsize, + (unsigned)percents, + dtbuf, + zip.fmt.crc32, + dst_fn); + total_size += zip.fmt.cmpsize; + } + total_usize += zip.fmt.ucmpsize; + goto skip_cmpsize; + } + + if (dst_fd == STDOUT_FILENO) { + /* Extracting to STDOUT */ + goto do_extract; + } + + /* Strip paths (after -l: unzip -lj a.zip lists full names) */ + if (opts & OPT_j) + overlapping_strcpy(dst_fn, bb_basename(dst_fn)); + /* Did this strip everything ("DIR/" case)? Then skip */ + if (!dst_fn[0]) + goto skip_cmpsize; + + if (last_char_is(dst_fn, '/')) { + int mode; + + /* Extract directory */ + mode = get_lstat_mode(dst_fn); + if (mode == -1) { /* ENOENT */ + if (!quiet) { + printf(" creating: %s\n", dst_fn); + } + unzip_create_leading_dirs(dst_fn); + if (bb_make_directory(dst_fn, dir_mode, FILEUTILS_IGNORE_CHMOD_ERR)) { + xfunc_die(); + } + } else { + if (!S_ISDIR(mode)) { + bb_error_msg_and_die("'%s' exists but is not a %s", + dst_fn, "directory"); + } + } + goto skip_cmpsize; + } + check_file: + /* Does target file already exist? */ + { + int mode = get_lstat_mode(dst_fn); + if (mode == -1) { + /* ENOENT: does not exist */ + goto do_open_and_extract; + } + if (overwrite == O_NEVER) { + goto skip_cmpsize; + } + if (!S_ISREG(mode)) { + fishy: + bb_error_msg_and_die("'%s' exists but is not a %s", + dst_fn, "regular file"); + } + if (overwrite == O_ALWAYS) { + goto do_open_and_extract; + } + printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); + my_fgets80(key_buf); + /* User input could take a long time. Is it still a regular file? */ + mode = get_lstat_mode(dst_fn); + if (!S_ISREG(mode)) + goto fishy; + } + + /* Extract (or skip) it */ + switch (key_buf[0]) { + case 'A': + overwrite = O_ALWAYS; + case 'y': /* Open file and fall into unzip */ + do_open_and_extract: + unzip_create_leading_dirs(dst_fn); +#if ENABLE_FEATURE_UNZIP_CDF + dst_fd = -1; + if (!S_ISLNK(file_mode)) { + dst_fd = xopen3(dst_fn, + O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, + file_mode); + } +#else + /* O_NOFOLLOW defends against symlink attacks */ + dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW); +#endif + do_extract: + if (!quiet) { + printf(/* zip.fmt.method == 0 + ? " extracting: %s\n" + : */ " inflating: %s\n", dst_fn); + } +#if ENABLE_FEATURE_UNZIP_CDF + if (S_ISLNK(file_mode)) { + if (dst_fd != STDOUT_FILENO) /* not -p? */ + unzip_extract_symlink(&symlink_placeholders, &zip, dst_fn); + } else +#endif + { + unzip_extract(&zip, dst_fd); + if (dst_fd != STDOUT_FILENO) { + /* closing STDOUT is potentially bad for future business */ + close(dst_fd); + } + } + break; + + case 'N': + overwrite = O_NEVER; + case 'n': /* Skip entry data */ + skip_cmpsize: + unzip_skip(zip.fmt.cmpsize); + break; + + case 'r': + /* Prompt for new name */ + printf("new name: "); + my_fgets80(key_buf); + free(dst_fn); + dst_fn = xstrdup(key_buf); + chomp(dst_fn); + goto check_file; + + default: + printf("error: invalid response [%c]\n", (char)key_buf[0]); + goto check_file; + } + + total_entries++; + } + +#if ENABLE_FEATURE_UNZIP_CDF + create_links_from_list(symlink_placeholders); +#endif + + if ((opts & OPT_l) && quiet <= 1) { + if (!verbose) { + // " 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 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); + } + } + + return 0; +} diff --git a/busybox/configs/TEST_nommu_defconfig b/busybox/configs/TEST_nommu_defconfig new file mode 100644 index 00000000000000..415f5a8027f9e1 --- /dev/null +++ b/busybox/configs/TEST_nommu_defconfig @@ -0,0 +1,912 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.16.0 +# Wed Jan 27 21:01:26 2010 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +CONFIG_DESKTOP=y +CONFIG_EXTRA_COMPAT=y +CONFIG_INCLUDE_SUSv2=y +# CONFIG_USE_PORTABLE_CODE is not set +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_LOCALE_SUPPORT is not set +# CONFIG_UNICODE_SUPPORT is not set +# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set +CONFIG_LONG_OPTS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +CONFIG_FEATURE_PIDFILE=y +CONFIG_FEATURE_SUID=y +CONFIG_FEATURE_SUID_CONFIG=y +CONFIG_FEATURE_SUID_CONFIG_QUIET=y +CONFIG_SELINUX=y +CONFIG_FEATURE_PREFER_APPLETS=y +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y + +# +# Build Options +# +# CONFIG_STATIC is not set +# CONFIG_PIE is not set +CONFIG_NOMMU=y +# CONFIG_BUILD_LIBBUSYBOX is not set +# CONFIG_FEATURE_INDIVIDUAL is not set +# CONFIG_FEATURE_SHARED_BUSYBOX is not set +CONFIG_LFS=y +CONFIG_CROSS_COMPILER_PREFIX="" +CONFIG_EXTRA_CFLAGS="" + +# +# 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 +# +# CONFIG_INSTALL_NO_USR is not set +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_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +CONFIG_FEATURE_FAST_TOP=y +CONFIG_FEATURE_ETC_NETWORKS=y +CONFIG_FEATURE_EDITING=y +CONFIG_FEATURE_EDITING_MAX_LEN=1024 +CONFIG_FEATURE_EDITING_VI=y +CONFIG_FEATURE_EDITING_HISTORY=15 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +CONFIG_FEATURE_TAB_COMPLETION=y +CONFIG_FEATURE_USERNAME_COMPLETION=y +CONFIG_FEATURE_EDITING_FANCY_PROMPT=y +CONFIG_FEATURE_EDITING_ASK_TERMINAL=y +CONFIG_FEATURE_NON_POSIX_CP=y +CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y +CONFIG_FEATURE_COPYBUF_KB=4 +CONFIG_MONOTONIC_SYSCALL=y +CONFIG_IOCTL_HEX2STR_ERROR=y +CONFIG_FEATURE_HWIB=y + +# +# Applets +# + +# +# Archival Utilities +# +CONFIG_FEATURE_SEAMLESS_LZMA=y +CONFIG_FEATURE_SEAMLESS_BZ2=y +CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_Z=y +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_BUNZIP2=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_GUNZIP=y +CONFIG_GZIP=y +CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LZOP=y +CONFIG_LZOP_COMPR_HIGH=y +CONFIG_RPM2CPIO=y +CONFIG_RPM=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_UNAME_GNAME=y +CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y +CONFIG_UNCOMPRESS=y +CONFIG_UNLZMA=y +CONFIG_FEATURE_LZMA_FAST=y +CONFIG_UNZIP=y + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAL=y +CONFIG_CAT=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_DATE=y +CONFIG_FEATURE_DATE_ISOFMT=y +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_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_HOSTID=y +CONFIG_ID=y +CONFIG_INSTALL=y +CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y +CONFIG_LN=y +CONFIG_LOGNAME=y +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_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_STAT=y +CONFIG_FEATURE_STAT_FORMAT=y +CONFIG_STTY=y +CONFIG_SUM=y +CONFIG_SYNC=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_FEATURE_TEST_64=y +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +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_WHO=y +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y + +# +# Console Utilities +# +CONFIG_CHVT=y +CONFIG_CLEAR=y +CONFIG_DEALLOCVT=y +CONFIG_DUMPKMAP=y +CONFIG_KBD_MODE=y +CONFIG_LOADFONT=y +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=y +CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y +CONFIG_DEFAULT_SETFONT_DIR="" +CONFIG_SETKEYCODES=y +CONFIG_SETLOGCONS=y +CONFIG_SHOWKEY=y + +# +# 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_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=y +CONFIG_FEATURE_VI_COLON=y +CONFIG_FEATURE_VI_YANKMARK=y +CONFIG_FEATURE_VI_SEARCH=y +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_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_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=y +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 + +# +# Init Utilities +# +CONFIG_INIT=y +CONFIG_FEATURE_USE_INITTAB=y +CONFIG_FEATURE_KILL_REMOVED=y +CONFIG_FEATURE_KILL_DELAY=1 +CONFIG_FEATURE_INIT_SCTTY=y +CONFIG_FEATURE_INIT_SYSLOG=y +CONFIG_FEATURE_INIT_QUIET=y +CONFIG_FEATURE_INIT_COREDUMPS=y +CONFIG_LINUXRC=y +CONFIG_HALT=y +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +CONFIG_MESG=y + +# +# Login/Password Management Utilities +# +CONFIG_FEATURE_SHADOWPASSWDS=y +CONFIG_USE_BB_PWD_GRP=y +CONFIG_USE_BB_SHADOW=y +CONFIG_USE_BB_CRYPT=y +CONFIG_USE_BB_CRYPT_SHA=y +CONFIG_ADDGROUP=y +CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y +CONFIG_FEATURE_ADDUSER_TO_GROUP=y +CONFIG_DELGROUP=y +CONFIG_FEATURE_DEL_USER_FROM_GROUP=y +CONFIG_FEATURE_CHECK_NAMES=y +CONFIG_ADDUSER=y +CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y +CONFIG_FIRST_SYSTEM_ID=100 +CONFIG_LAST_SYSTEM_ID=999 +CONFIG_DELUSER=y +CONFIG_GETTY=y +CONFIG_FEATURE_UTMP=y +CONFIG_FEATURE_WTMP=y +CONFIG_LOGIN=y +# CONFIG_PAM 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_CRYPTPW=y +CONFIG_CHPASSWD=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 + +# +# Linux Module Utilities +# +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_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_DEFAULT_MODULES_DIR="/lib/modules" +CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" + +# +# Linux System Utilities +# +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +CONFIG_BLKID=y +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=y +CONFIG_FEATURE_SGI_LABEL=y +CONFIG_FEATURE_SUN_LABEL=y +CONFIG_FEATURE_OSF_LABEL=y +CONFIG_FEATURE_FDISK_ADVANCED=y +CONFIG_FINDFS=y +CONFIG_FREERAMDISK=y +CONFIG_FSCK_MINIX=y +CONFIG_MKFS_EXT2=y +CONFIG_MKFS_MINIX=y + +# +# Minix filesystem support +# +CONFIG_FEATURE_MINIX2=y +CONFIG_MKFS_REISER=y +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=y +CONFIG_IPCRM=y +CONFIG_IPCS=y +CONFIG_LOSETUP=y +CONFIG_LSPCI=y +CONFIG_LSUSB=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_MKSWAP=y +CONFIG_FEATURE_MKSWAP_UUID=y +CONFIG_MORE=y +CONFIG_VOLUMEID=y +CONFIG_FEATURE_VOLUMEID_EXT=y +CONFIG_FEATURE_VOLUMEID_BTRFS=y +CONFIG_FEATURE_VOLUMEID_REISERFS=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_LINUXSWAP=y +CONFIG_FEATURE_VOLUMEID_CRAMFS=y +CONFIG_FEATURE_VOLUMEID_ROMFS=y +CONFIG_FEATURE_VOLUMEID_SYSV=y +CONFIG_FEATURE_VOLUMEID_OCFS2=y +CONFIG_FEATURE_VOLUMEID_LINUXRAID=y +CONFIG_MOUNT=y +CONFIG_FEATURE_MOUNT_FAKE=y +CONFIG_FEATURE_MOUNT_VERBOSE=y +CONFIG_FEATURE_MOUNT_HELPERS=y +CONFIG_FEATURE_MOUNT_LABEL=y +CONFIG_FEATURE_MOUNT_NFS=y +CONFIG_FEATURE_MOUNT_CIFS=y +CONFIG_FEATURE_MOUNT_FLAGS=y +CONFIG_FEATURE_MOUNT_FSTAB=y +CONFIG_PIVOT_ROOT=y +CONFIG_RDATE=y +CONFIG_RDEV=y +CONFIG_READPROFILE=y +CONFIG_RTCWAKE=y +CONFIG_SCRIPT=y +CONFIG_SCRIPTREPLAY=y +CONFIG_SETARCH=y +CONFIG_SWAPONOFF=y +CONFIG_FEATURE_SWAPON_PRI=y +CONFIG_SWITCH_ROOT=y +CONFIG_UMOUNT=y +CONFIG_FEATURE_UMOUNT_ALL=y + +# +# Common options for mount/umount +# +CONFIG_FEATURE_MOUNT_LOOP=y +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Miscellaneous Utilities +# +CONFIG_ADJTIMEX=y +CONFIG_BBCONFIG=y +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=y +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_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 +# 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=y +CONFIG_FBSPLASH=y +CONFIG_FLASHCP=y +# CONFIG_FLASH_LOCK is not set +# CONFIG_FLASH_UNLOCK is not set +# CONFIG_FLASH_ERASEALL is not set +CONFIG_IONICE=y +CONFIG_INOTIFYD=y +CONFIG_LAST=y +CONFIG_FEATURE_LAST_SMALL=y +# CONFIG_FEATURE_LAST_FANCY is not set +CONFIG_LESS=y +CONFIG_FEATURE_LESS_MAXLINES=9999999 +CONFIG_FEATURE_LESS_BRACKETS=y +CONFIG_FEATURE_LESS_FLAGS=y +CONFIG_FEATURE_LESS_MARKS=y +CONFIG_FEATURE_LESS_REGEXP=y +CONFIG_FEATURE_LESS_WINCH=y +CONFIG_FEATURE_LESS_DASHCMD=y +CONFIG_FEATURE_LESS_LINENUMS=y +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=y +CONFIG_RAIDAUTORUN=y +CONFIG_READAHEAD=y +CONFIG_RUNLEVEL=y +CONFIG_RX=y +CONFIG_SETSID=y +CONFIG_STRINGS=y +CONFIG_TASKSET=y +CONFIG_FEATURE_TASKSET_FANCY=y +CONFIG_TIME=y +CONFIG_TIMEOUT=y +CONFIG_TTYSIZE=y +CONFIG_VOLNAME=y +CONFIG_WALL=y +CONFIG_WATCHDOG=y + +# +# Networking Utilities +# +CONFIG_FEATURE_IPV6=y +CONFIG_FEATURE_UNIX_LOCAL=y +CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y +CONFIG_VERBOSE_RESOLUTION_ERRORS=y +CONFIG_ARP=y +CONFIG_ARPING=y +CONFIG_BRCTL=y +CONFIG_FEATURE_BRCTL_FANCY=y +CONFIG_FEATURE_BRCTL_SHOW=y +CONFIG_DNSD=y +CONFIG_ETHER_WAKE=y +CONFIG_FAKEIDENTD=y +CONFIG_FTPD=y +CONFIG_FEATURE_FTP_WRITE=y +CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=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_USE_SENDFILE=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_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +CONFIG_FEATURE_IFCONFIG_SLIP=y +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y +CONFIG_IFENSLAVE=y +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=y +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=y +CONFIG_IP=y +CONFIG_FEATURE_IP_ADDRESS=y +CONFIG_FEATURE_IP_LINK=y +CONFIG_FEATURE_IP_ROUTE=y +CONFIG_FEATURE_IP_TUNNEL=y +CONFIG_FEATURE_IP_RULE=y +CONFIG_FEATURE_IP_SHORT_FORMS=y +CONFIG_FEATURE_IP_RARE_PROTOCOLS=y +CONFIG_IPADDR=y +CONFIG_IPLINK=y +CONFIG_IPROUTE=y +CONFIG_IPTUNNEL=y +CONFIG_IPRULE=y +CONFIG_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y +CONFIG_NAMEIF=y +CONFIG_FEATURE_NAMEIF_EXTENDED=y +CONFIG_NC=y +CONFIG_NC_SERVER=y +CONFIG_NC_EXTRA=y +CONFIG_NETSTAT=y +CONFIG_FEATURE_NETSTAT_WIDE=y +CONFIG_FEATURE_NETSTAT_PRG=y +CONFIG_NSLOOKUP=y +CONFIG_NTPD=y +CONFIG_FEATURE_NTPD_SERVER=y +CONFIG_PING=y +CONFIG_PING6=y +CONFIG_FEATURE_FANCY_PING=y +CONFIG_PSCAN=y +CONFIG_ROUTE=y +CONFIG_SLATTACH=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 +CONFIG_FEATURE_TFTP_GET=y +CONFIG_FEATURE_TFTP_PUT=y +CONFIG_FEATURE_TFTP_BLOCKSIZE=y +CONFIG_FEATURE_TFTP_PROGRESS_BAR=y +CONFIG_TFTP_DEBUG=y +CONFIG_TRACEROUTE=y +CONFIG_TRACEROUTE6=y +CONFIG_FEATURE_TRACEROUTE_VERBOSE=y +CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y +CONFIG_UDHCPD=y +CONFIG_DHCPRELAY=y +CONFIG_DUMPLEASES=y +CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y +CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases" +CONFIG_UDHCPC=y +CONFIG_FEATURE_UDHCPC_ARPING=y +CONFIG_FEATURE_UDHCP_PORT=y +CONFIG_UDHCP_DEBUG=9 +CONFIG_FEATURE_UDHCP_RFC3397=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_VCONFIG=y +CONFIG_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +CONFIG_FEATURE_WGET_LONG_OPTIONS=y +CONFIG_ZCIP=y +CONFIG_TCPSVD=y +CONFIG_TUNCTL=y +CONFIG_FEATURE_TUNCTL_UG=y +CONFIG_UDPSVD=y + +# +# 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_FREE=y +CONFIG_FUSER=y +CONFIG_KILL=y +CONFIG_KILLALL=y +CONFIG_KILLALL5=y +CONFIG_NMETER=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=y +CONFIG_FEATURE_PS_TIME=y +CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y +CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS=y +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_UPTIME=y +CONFIG_WATCH=y + +# +# Runit Utilities +# +CONFIG_RUNSV=y +CONFIG_RUNSVDIR=y +CONFIG_FEATURE_RUNSVDIR_LOG=y +CONFIG_SV=y +CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service" +CONFIG_SVLOGD=y +CONFIG_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=y + +# +# SELinux Utilities +# +CONFIG_CHCON=y +CONFIG_FEATURE_CHCON_LONG_OPTIONS=y +CONFIG_GETENFORCE=y +CONFIG_GETSEBOOL=y +CONFIG_LOAD_POLICY=y +CONFIG_MATCHPATHCON=y +CONFIG_RESTORECON=y +CONFIG_RUNCON=y +CONFIG_FEATURE_RUNCON_LONG_OPTIONS=y +CONFIG_SELINUXENABLED=y +CONFIG_SETENFORCE=y +CONFIG_SETFILES=y +CONFIG_FEATURE_SETFILES_CHECK_OPTION=y +CONFIG_SETSEBOOL=y +CONFIG_SESTATUS=y + +# +# Shells +# +# CONFIG_FEATURE_SH_IS_ASH is not set +CONFIG_FEATURE_SH_IS_HUSH=y +# CONFIG_FEATURE_SH_IS_NONE is not set +# CONFIG_ASH is not set +# CONFIG_ASH_BASH_COMPAT is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_ECHO is not set +# CONFIG_ASH_PRINTF is not set +# CONFIG_ASH_TEST is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +CONFIG_HUSH=y +CONFIG_HUSH_BASH_COMPAT=y +CONFIG_HUSH_HELP=y +CONFIG_HUSH_INTERACTIVE=y +CONFIG_HUSH_JOB=y +CONFIG_HUSH_TICK=y +CONFIG_HUSH_IF=y +CONFIG_HUSH_LOOPS=y +CONFIG_HUSH_CASE=y +CONFIG_HUSH_FUNCTIONS=y +CONFIG_HUSH_LOCAL=y +CONFIG_HUSH_EXPORT_N=y +CONFIG_HUSH_RANDOM_SUPPORT=y +CONFIG_SH_MATH_SUPPORT=y +CONFIG_SH_MATH_SUPPORT_64=y +CONFIG_FEATURE_SH_EXTRA_QUIET=y +CONFIG_FEATURE_SH_STANDALONE=y +CONFIG_FEATURE_SH_NOFORK=y +CONFIG_CTTYHACK=y + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +CONFIG_FEATURE_ROTATE_LOGFILE=y +CONFIG_FEATURE_REMOTE_LOG=y +CONFIG_FEATURE_SYSLOGD_DUP=y +CONFIG_FEATURE_IPC_SYSLOG=y +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 +CONFIG_LOGREAD=y +CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y +CONFIG_KLOGD=y +CONFIG_LOGGER=y diff --git a/busybox/configs/TEST_noprintf_defconfig b/busybox/configs/TEST_noprintf_defconfig new file mode 100644 index 00000000000000..9b378fd5580559 --- /dev/null +++ b/busybox/configs/TEST_noprintf_defconfig @@ -0,0 +1,920 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.17.0.git +# Mon Jun 7 13:37:55 2010 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +CONFIG_DESKTOP=y +CONFIG_EXTRA_COMPAT=y +CONFIG_INCLUDE_SUSv2=y +# CONFIG_USE_PORTABLE_CODE is not set +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 is not set +# CONFIG_FEATURE_INSTALLER 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=y +CONFIG_SUBST_WCHAR=63 +CONFIG_LAST_SUPPORTED_WCHAR=65535 +CONFIG_UNICODE_COMBINING_WCHARS=y +CONFIG_UNICODE_WIDE_WCHARS=y +CONFIG_UNICODE_BIDI_SUPPORT=y +# CONFIG_UNICODE_NEUTRAL_TABLE is not set +CONFIG_UNICODE_PRESERVE_BROKEN=y +CONFIG_LONG_OPTS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +CONFIG_FEATURE_UTMP=y +CONFIG_FEATURE_WTMP=y +CONFIG_FEATURE_PIDFILE=y +# CONFIG_FEATURE_SUID is not set +# CONFIG_FEATURE_SUID_CONFIG is not set +# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +# CONFIG_FEATURE_SYSLOG is not set + +# +# Build Options +# +CONFIG_STATIC=y +# 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=y +CONFIG_CROSS_COMPILER_PREFIX="i486-linux-uclibc-" +CONFIG_EXTRA_CFLAGS="" + +# +# Debugging Options +# +# CONFIG_DEBUG is not set +# CONFIG_DEBUG_PESSIMIZE is not set +CONFIG_WERROR=y +CONFIG_NO_DEBUG_LIB=y +# CONFIG_DMALLOC is not set +# CONFIG_EFENCE is not set + +# +# Installation Options +# +# CONFIG_INSTALL_NO_USR is not set +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_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +CONFIG_FEATURE_FAST_TOP=y +# CONFIG_FEATURE_ETC_NETWORKS is not set +CONFIG_FEATURE_EDITING=y +CONFIG_FEATURE_EDITING_MAX_LEN=1024 +CONFIG_FEATURE_EDITING_VI=y +CONFIG_FEATURE_EDITING_HISTORY=15 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +CONFIG_FEATURE_TAB_COMPLETION=y +CONFIG_FEATURE_USERNAME_COMPLETION=y +CONFIG_FEATURE_EDITING_FANCY_PROMPT=y +CONFIG_FEATURE_EDITING_ASK_TERMINAL=y +CONFIG_FEATURE_NON_POSIX_CP=y +# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set +CONFIG_FEATURE_COPYBUF_KB=64 +CONFIG_MONOTONIC_SYSCALL=y +# CONFIG_IOCTL_HEX2STR_ERROR is not set +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=y +# CONFIG_AR is not set +# CONFIG_FEATURE_AR_LONG_FILENAMES is not set +# CONFIG_FEATURE_AR_CREATE is not set +# CONFIG_BUNZIP2 is not set +# CONFIG_BZIP2 is not set +# CONFIG_CPIO is not set +# CONFIG_FEATURE_CPIO_O is not set +# CONFIG_FEATURE_CPIO_P is not set +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +# CONFIG_GUNZIP is not set +# CONFIG_GZIP is not set +# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set +# CONFIG_LZOP is not set +# CONFIG_LZOP_COMPR_HIGH is not set +# CONFIG_RPM2CPIO is not set +# CONFIG_RPM is not set +# CONFIG_TAR is not set +# CONFIG_FEATURE_TAR_CREATE is not set +# CONFIG_FEATURE_TAR_AUTODETECT is not set +# CONFIG_FEATURE_TAR_FROM is not set +# CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY is not set +# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set +# CONFIG_FEATURE_TAR_GNU_EXTENSIONS is not set +# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set +# CONFIG_FEATURE_TAR_UNAME_GNAME is not set +# CONFIG_FEATURE_TAR_NOPRESERVE_TIME is not set +# CONFIG_FEATURE_TAR_SELINUX is not set +# CONFIG_UNCOMPRESS is not set +# CONFIG_UNLZMA is not set +# CONFIG_FEATURE_LZMA_FAST is not set +# CONFIG_LZMA is not set +# CONFIG_UNXZ is not set +# CONFIG_XZ is not set +# CONFIG_UNZIP is not set + +# +# Coreutils +# +CONFIG_BASENAME=y +# CONFIG_CAT is not set +# CONFIG_DATE is not set +# CONFIG_FEATURE_DATE_ISOFMT is not set +# CONFIG_FEATURE_DATE_NANO is not set +# CONFIG_FEATURE_DATE_COMPAT is not set +# CONFIG_TEST is not set +# CONFIG_FEATURE_TEST_64 is not set +# CONFIG_TR is not set +# CONFIG_FEATURE_TR_CLASSES is not set +# CONFIG_FEATURE_TR_EQUIV is not set +# CONFIG_CAL is not set +# CONFIG_CATV is not set +# CONFIG_CHGRP is not set +# CONFIG_CHMOD is not set +# CONFIG_CHOWN is not set +# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set +# CONFIG_CHROOT is not set +# CONFIG_CKSUM is not set +# CONFIG_COMM is not set +# CONFIG_CP is not set +# CONFIG_FEATURE_CP_LONG_OPTIONS is not set +# CONFIG_CUT is not set +# CONFIG_DD is not set +# CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set +# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set +# CONFIG_FEATURE_DD_IBS_OBS is not set +# CONFIG_DF is not set +# CONFIG_FEATURE_DF_FANCY is not set +# CONFIG_DIRNAME is not set +# CONFIG_DOS2UNIX is not set +# CONFIG_UNIX2DOS is not set +# CONFIG_DU is not set +# CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K is not set +# CONFIG_ECHO is not set +# CONFIG_FEATURE_FANCY_ECHO is not set +# CONFIG_ENV is not set +# CONFIG_FEATURE_ENV_LONG_OPTIONS is not set +# CONFIG_EXPAND is not set +# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set +# CONFIG_EXPR is not set +# CONFIG_EXPR_MATH_SUPPORT_64 is not set +CONFIG_FALSE=y +# CONFIG_FOLD is not set +# CONFIG_FSYNC is not set +# CONFIG_HEAD is not set +# CONFIG_FEATURE_FANCY_HEAD is not set +# CONFIG_HOSTID is not set +# CONFIG_ID is not set +# CONFIG_INSTALL is not set +# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set +# CONFIG_LN is not set +# CONFIG_LOGNAME is not set +# CONFIG_LS is not set +# CONFIG_FEATURE_LS_FILETYPES is not set +# CONFIG_FEATURE_LS_FOLLOWLINKS is not set +# CONFIG_FEATURE_LS_RECURSIVE is not set +# CONFIG_FEATURE_LS_SORTFILES is not set +# CONFIG_FEATURE_LS_TIMESTAMPS is not set +# CONFIG_FEATURE_LS_USERNAME is not set +# CONFIG_FEATURE_LS_COLOR is not set +# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set +# CONFIG_MD5SUM is not set +# CONFIG_MKDIR is not set +# CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set +# CONFIG_MKFIFO is not set +# CONFIG_MKNOD is not set +# CONFIG_MV is not set +# CONFIG_FEATURE_MV_LONG_OPTIONS is not set +# CONFIG_NICE is not set +# CONFIG_NOHUP is not set +# CONFIG_OD is not set +# CONFIG_PRINTENV is not set +# CONFIG_PRINTF is not set +# CONFIG_PWD is not set +# CONFIG_READLINK is not set +# CONFIG_FEATURE_READLINK_FOLLOW is not set +# CONFIG_REALPATH is not set +# CONFIG_RM is not set +# CONFIG_RMDIR is not set +# CONFIG_FEATURE_RMDIR_LONG_OPTIONS is not set +# CONFIG_SEQ is not set +# CONFIG_SHA1SUM is not set +# CONFIG_SHA256SUM is not set +# CONFIG_SHA512SUM is not set +# CONFIG_SLEEP is not set +# CONFIG_FEATURE_FANCY_SLEEP is not set +# CONFIG_FEATURE_FLOAT_SLEEP is not set +# CONFIG_SORT is not set +# CONFIG_FEATURE_SORT_BIG is not set +# CONFIG_SPLIT is not set +# CONFIG_FEATURE_SPLIT_FANCY is not set +# CONFIG_STAT is not set +# CONFIG_FEATURE_STAT_FORMAT is not set +# CONFIG_STTY is not set +# CONFIG_SUM is not set +# CONFIG_SYNC is not set +# CONFIG_TAC is not set +# CONFIG_TAIL is not set +# CONFIG_FEATURE_FANCY_TAIL is not set +# CONFIG_TEE is not set +# CONFIG_FEATURE_TEE_USE_BLOCK_IO is not set +# CONFIG_TOUCH is not set +CONFIG_TRUE=y +# CONFIG_TTY is not set +# CONFIG_UNAME is not set +# CONFIG_UNEXPAND is not set +# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set +# CONFIG_UNIQ is not set +# CONFIG_USLEEP is not set +# CONFIG_UUDECODE is not set +# CONFIG_UUENCODE is not set +# CONFIG_WC is not set +# CONFIG_FEATURE_WC_LARGE is not set +# CONFIG_WHO is not set +# CONFIG_WHOAMI is not set +# CONFIG_YES is not set +# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set +# CONFIG_FEATURE_HUMAN_READABLE is not set +# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set + +# +# Console Utilities +# +# CONFIG_CHVT is not set +# CONFIG_FGCONSOLE is not set +# CONFIG_CLEAR is not set +# CONFIG_DEALLOCVT is not set +# CONFIG_DUMPKMAP is not set +# CONFIG_KBD_MODE is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +# CONFIG_OPENVT is not set +# CONFIG_RESET is not set +# CONFIG_RESIZE is not set +# CONFIG_FEATURE_RESIZE_PRINT is not set +# CONFIG_SETCONSOLE is not set +# 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 is not set +# 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 +# +# CONFIG_MKTEMP is not set +CONFIG_PIPE_PROGRESS=y +# CONFIG_RUN_PARTS is not set +# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set +# CONFIG_FEATURE_RUN_PARTS_FANCY is not set +# CONFIG_START_STOP_DAEMON is not set +# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set +# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set +# CONFIG_WHICH is not set + +# +# Editors +# +# CONFIG_AWK is not set +# CONFIG_FEATURE_AWK_LIBM is not set +# CONFIG_CMP is not set +# CONFIG_DIFF is not set +# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set +# CONFIG_FEATURE_DIFF_DIR is not set +# CONFIG_ED is not set +# CONFIG_PATCH is not set +# CONFIG_SED is not set +# CONFIG_VI is not set +CONFIG_FEATURE_VI_MAX_LEN=0 +# CONFIG_FEATURE_VI_8BIT is not set +# CONFIG_FEATURE_VI_COLON is not set +# CONFIG_FEATURE_VI_YANKMARK is not set +# CONFIG_FEATURE_VI_SEARCH is not set +# CONFIG_FEATURE_VI_USE_SIGNALS is not set +# CONFIG_FEATURE_VI_DOT_CMD is not set +# CONFIG_FEATURE_VI_READONLY is not set +# CONFIG_FEATURE_VI_SETOPTS is not set +# CONFIG_FEATURE_VI_SET is not set +# CONFIG_FEATURE_VI_WIN_RESIZE is not set +# CONFIG_FEATURE_VI_ASK_TERMINAL is not set +# CONFIG_FEATURE_ALLOW_EXEC is not set + +# +# Finding Utilities +# +# CONFIG_FIND is not set +# CONFIG_FEATURE_FIND_PRINT0 is not set +# CONFIG_FEATURE_FIND_MTIME is not set +# CONFIG_FEATURE_FIND_MMIN is not set +# CONFIG_FEATURE_FIND_PERM is not set +# CONFIG_FEATURE_FIND_TYPE is not set +# CONFIG_FEATURE_FIND_XDEV is not set +# CONFIG_FEATURE_FIND_MAXDEPTH is not set +# CONFIG_FEATURE_FIND_NEWER is not set +# CONFIG_FEATURE_FIND_INUM is not set +# CONFIG_FEATURE_FIND_EXEC is not set +# CONFIG_FEATURE_FIND_USER is not set +# CONFIG_FEATURE_FIND_GROUP is not set +# CONFIG_FEATURE_FIND_NOT is not set +# CONFIG_FEATURE_FIND_DEPTH is not set +# CONFIG_FEATURE_FIND_PAREN is not set +# CONFIG_FEATURE_FIND_SIZE is not set +# CONFIG_FEATURE_FIND_PRUNE is not set +# CONFIG_FEATURE_FIND_DELETE is not set +# CONFIG_FEATURE_FIND_PATH is not set +# CONFIG_FEATURE_FIND_REGEX is not set +# CONFIG_FEATURE_FIND_CONTEXT is not set +# CONFIG_FEATURE_FIND_LINKS is not set +# CONFIG_GREP is not set +# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_CONTEXT is not set +# CONFIG_XARGS is not set +# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set +# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set +# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set +# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set + +# +# Init Utilities +# +# CONFIG_INIT is not set +# CONFIG_FEATURE_USE_INITTAB is not set +# CONFIG_FEATURE_KILL_REMOVED is not set +CONFIG_FEATURE_KILL_DELAY=0 +# CONFIG_FEATURE_INIT_SCTTY is not set +# CONFIG_FEATURE_INIT_SYSLOG is not set +# CONFIG_FEATURE_INIT_QUIET is not set +# CONFIG_FEATURE_INIT_COREDUMPS is not set +# CONFIG_LINUXRC is not set +# CONFIG_HALT is not set +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +# CONFIG_MESG is not set +# CONFIG_BOOTCHARTD is not set + +# +# 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 is not set +# CONFIG_USE_BB_CRYPT_SHA 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_DELGROUP is not set +# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set +# CONFIG_FEATURE_CHECK_NAMES is not set +# CONFIG_ADDUSER is not set +# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set +CONFIG_FIRST_SYSTEM_ID=0 +CONFIG_LAST_SYSTEM_ID=0 +# CONFIG_DELUSER is not set +# CONFIG_GETTY is not set +# CONFIG_LOGIN 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_SU is not set +# CONFIG_FEATURE_SU_SYSLOG is not set +# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Linux Ext2 FS Progs +# +# CONFIG_CHATTR is not set +# CONFIG_FSCK is not set +# CONFIG_LSATTR is not set +# CONFIG_TUNE2FS is not set + +# +# Linux Module Utilities +# +# CONFIG_MODINFO is not set +# CONFIG_MODPROBE_SMALL is not set +# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set +# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set +# 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="" +CONFIG_DEFAULT_DEPMOD_FILE="" + +# +# Linux System Utilities +# +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +# CONFIG_BLKID is not set +# CONFIG_DMESG is not set +# CONFIG_FEATURE_DMESG_PRETTY is not set +# CONFIG_FBSET is not set +# CONFIG_FEATURE_FBSET_FANCY is not set +# CONFIG_FEATURE_FBSET_READMODE is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +CONFIG_FDISK_SUPPORT_LARGE_DISKS=y +# CONFIG_FEATURE_FDISK_WRITABLE is not set +# 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_FDISK_ADVANCED is not set +# CONFIG_FINDFS is not set +# CONFIG_FLOCK is not set +# CONFIG_FREERAMDISK is not set +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_EXT2 is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_FEATURE_MINIX2 is not set +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +# CONFIG_GETOPT is not set +# CONFIG_FEATURE_GETOPT_LONG is not set +# CONFIG_HEXDUMP is not set +# CONFIG_FEATURE_HEXDUMP_REVERSE is not set +# CONFIG_HD is not set +# CONFIG_HWCLOCK is not set +# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set +# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set +# CONFIG_IPCRM is not set +# CONFIG_IPCS is not set +# CONFIG_LOSETUP is not set +# CONFIG_LSPCI is not set +# CONFIG_LSUSB is not set +# 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_MKSWAP is not set +# CONFIG_FEATURE_MKSWAP_UUID is not set +# CONFIG_MORE is not set +CONFIG_VOLUMEID=y +# CONFIG_FEATURE_VOLUMEID_EXT is not set +# CONFIG_FEATURE_VOLUMEID_BTRFS is not set +# CONFIG_FEATURE_VOLUMEID_REISERFS is not set +# CONFIG_FEATURE_VOLUMEID_FAT is not set +# CONFIG_FEATURE_VOLUMEID_HFS is not set +# CONFIG_FEATURE_VOLUMEID_JFS is not set +# CONFIG_FEATURE_VOLUMEID_XFS is not set +# CONFIG_FEATURE_VOLUMEID_NTFS is not set +# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set +# CONFIG_FEATURE_VOLUMEID_UDF is not set +# CONFIG_FEATURE_VOLUMEID_LUKS is not set +# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set +# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set +# CONFIG_FEATURE_VOLUMEID_ROMFS is not set +# CONFIG_FEATURE_VOLUMEID_SYSV is not set +# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set +# CONFIG_FEATURE_VOLUMEID_LINUXRAID 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_PIVOT_ROOT is not set +# CONFIG_RDATE is not set +# CONFIG_RDEV is not set +# CONFIG_READPROFILE is not set +# CONFIG_RTCWAKE is not set +# CONFIG_SCRIPT is not set +# CONFIG_SCRIPTREPLAY is not set +# CONFIG_SETARCH is not set +# CONFIG_SWAPONOFF is not set +# CONFIG_FEATURE_SWAPON_PRI is not set +# CONFIG_SWITCH_ROOT is not set +# CONFIG_UMOUNT is not set +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Miscellaneous Utilities +# +# CONFIG_ADJTIMEX is not set +CONFIG_BBCONFIG=y +# CONFIG_BEEP is not set +CONFIG_FEATURE_BEEP_FREQ=0 +CONFIG_FEATURE_BEEP_LENGTH_MS=0 +# CONFIG_CHAT is not set +# CONFIG_FEATURE_CHAT_NOFAIL is not set +# CONFIG_FEATURE_CHAT_TTY_HIFI is not set +# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set +# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set +# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set +# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set +# CONFIG_FEATURE_CHAT_CLR_ABORT is not set +# CONFIG_CHRT is not set +# CONFIG_CROND is not set +# CONFIG_FEATURE_CROND_D is not set +# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set +CONFIG_FEATURE_CROND_DIR="" +# CONFIG_CRONTAB is not set +# CONFIG_DC is not set +# CONFIG_FEATURE_DC_LIBM is not set +# 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 is not set +# CONFIG_EJECT is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +# CONFIG_FBSPLASH is not set +# 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 is not set +# 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_LESS is not set +CONFIG_FEATURE_LESS_MAXLINES=0 +# CONFIG_FEATURE_LESS_BRACKETS is not set +# CONFIG_FEATURE_LESS_FLAGS is not set +# CONFIG_FEATURE_LESS_MARKS is not set +# CONFIG_FEATURE_LESS_REGEXP is not set +# CONFIG_FEATURE_LESS_WINCH is not set +# CONFIG_FEATURE_LESS_DASHCMD is not set +# CONFIG_FEATURE_LESS_LINENUMS is not set +# CONFIG_HDPARM is not set +# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set +# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set +# 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_MAKEDEVS is not set +# CONFIG_FEATURE_MAKEDEVS_LEAF is not set +# CONFIG_FEATURE_MAKEDEVS_TABLE is not set +# CONFIG_MAN is not set +# CONFIG_MICROCOM is not set +# CONFIG_MOUNTPOINT is not set +# CONFIG_MT is not set +# CONFIG_RAIDAUTORUN is not set +# CONFIG_READAHEAD is not set +# CONFIG_RFKILL is not set +# CONFIG_RUNLEVEL is not set +# CONFIG_RX is not set +# CONFIG_SETSID is not set +# CONFIG_STRINGS is not set +# CONFIG_TASKSET is not set +# CONFIG_FEATURE_TASKSET_FANCY is not set +# CONFIG_TIME is not set +# CONFIG_TIMEOUT is not set +# CONFIG_TTYSIZE is not set +# CONFIG_VOLNAME is not set +# CONFIG_WALL is not set +# CONFIG_WATCHDOG is not set + +# +# Networking Utilities +# +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_FEATURE_UNIX_LOCAL is not set +# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +# CONFIG_ARP is not set +# CONFIG_ARPING is not set +# CONFIG_BRCTL is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +# CONFIG_DNSD is not set +# 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_ACCEPT_BROKEN_LIST is not set +# CONFIG_FTPGET is not set +# CONFIG_FTPPUT is not set +# CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set +# CONFIG_HOSTNAME is not set +# 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 +# CONFIG_FEATURE_HTTPD_CGI is not set +# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set +# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set +# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set +# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set +# CONFIG_FEATURE_HTTPD_PROXY is not set +# 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 is not set +# CONFIG_IFUPDOWN is not set +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 +# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set +# CONFIG_INETD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set +# CONFIG_FEATURE_INETD_RPC is not set +# CONFIG_IP is not set +# CONFIG_FEATURE_IP_ADDRESS is not set +# CONFIG_FEATURE_IP_LINK is not set +# CONFIG_FEATURE_IP_ROUTE is not set +# CONFIG_FEATURE_IP_TUNNEL is not set +# CONFIG_FEATURE_IP_RULE is not set +# CONFIG_FEATURE_IP_SHORT_FORMS is not set +# 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_NAMEIF is not set +# CONFIG_FEATURE_NAMEIF_EXTENDED is not set +# CONFIG_NC is not set +# CONFIG_NC_SERVER is not set +# CONFIG_NC_EXTRA is not set +# CONFIG_NETSTAT is not set +# CONFIG_FEATURE_NETSTAT_WIDE is not set +# CONFIG_FEATURE_NETSTAT_PRG is not set +# CONFIG_NSLOOKUP is not set +# CONFIG_NTPD is not set +# CONFIG_FEATURE_NTPD_SERVER is not set +# CONFIG_PING is not set +# CONFIG_PING6 is not set +# CONFIG_FEATURE_FANCY_PING is not set +# CONFIG_PSCAN is not set +# CONFIG_ROUTE is not set +# CONFIG_SLATTACH is not set +# CONFIG_TCPSVD is not set +# CONFIG_TELNET is not set +# CONFIG_FEATURE_TELNET_TTYPE is not set +# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set +# CONFIG_TELNETD is not set +# CONFIG_FEATURE_TELNETD_STANDALONE is not set +# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set +# CONFIG_TFTP is not set +# CONFIG_TFTPD is not set +# CONFIG_FEATURE_TFTP_GET is not set +# CONFIG_FEATURE_TFTP_PUT is not set +# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set +# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set +# CONFIG_TFTP_DEBUG is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_TRACEROUTE6 is not set +# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set +# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set +# CONFIG_TUNCTL is not set +# CONFIG_FEATURE_TUNCTL_UG is not set +# CONFIG_UDHCPD is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +CONFIG_DHCPD_LEASES_FILE="" +# CONFIG_UDHCPC is not set +# CONFIG_FEATURE_UDHCPC_ARPING is not set +# CONFIG_FEATURE_UDHCP_PORT is not set +CONFIG_UDHCP_DEBUG=0 +# CONFIG_FEATURE_UDHCP_RFC3397 is not set +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 is not set +# CONFIG_FEATURE_WGET_STATUSBAR is not set +# CONFIG_FEATURE_WGET_AUTHENTICATION is not set +# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set +# CONFIG_ZCIP is not set + +# +# Print Utilities +# +# CONFIG_LPD is not set +# CONFIG_LPR is not set +# CONFIG_LPQ is not set + +# +# 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 + +# +# Process Utilities +# +# CONFIG_FREE is not set +# CONFIG_FUSER is not set +# CONFIG_KILL is not set +# CONFIG_KILLALL is not set +# CONFIG_KILLALL5 is not set +# CONFIG_NMETER is not set +# CONFIG_PGREP is not set +# CONFIG_PIDOF is not set +# CONFIG_FEATURE_PIDOF_SINGLE is not set +# CONFIG_FEATURE_PIDOF_OMIT is not set +# CONFIG_PKILL is not set +# CONFIG_PS is not set +# CONFIG_FEATURE_PS_WIDE is not set +# CONFIG_FEATURE_PS_TIME is not set +# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set +# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set +# CONFIG_RENICE is not set +# CONFIG_BB_SYSCTL is not set +# CONFIG_TOP is not set +# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set +# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set +# CONFIG_FEATURE_TOP_SMP_CPU is not set +# CONFIG_FEATURE_TOP_DECIMALS is not set +# CONFIG_FEATURE_TOP_SMP_PROCESS is not set +# CONFIG_FEATURE_TOPMEM is not set +# CONFIG_FEATURE_SHOW_THREADS is not set +# CONFIG_UPTIME is not set +# CONFIG_WATCH is not set + +# +# Runit Utilities +# +# 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_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 +# 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 is not set +# CONFIG_ASH_BASH_COMPAT is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_ECHO is not set +# CONFIG_ASH_PRINTF is not set +# CONFIG_ASH_TEST is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +# CONFIG_HUSH is not set +# CONFIG_HUSH_BASH_COMPAT is not set +# CONFIG_HUSH_HELP is not set +# CONFIG_HUSH_INTERACTIVE 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_EXPORT_N is not set +# CONFIG_HUSH_RANDOM_SUPPORT is not set +# CONFIG_FEATURE_SH_IS_ASH is not set +# CONFIG_FEATURE_SH_IS_HUSH is not set +CONFIG_FEATURE_SH_IS_NONE=y +# 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 is not set +# CONFIG_SH_MATH_SUPPORT_64 is not set +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set +# CONFIG_CTTYHACK is not set + +# +# System Logging Utilities +# +# 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_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_KLOGD is not set +# CONFIG_LOGGER is not set diff --git a/busybox/configs/TEST_rh9_defconfig b/busybox/configs/TEST_rh9_defconfig new file mode 100644 index 00000000000000..4ac62b9da47275 --- /dev/null +++ b/busybox/configs/TEST_rh9_defconfig @@ -0,0 +1,927 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.17.0.git +# Fri Apr 16 22:25:22 2010 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +# CONFIG_DESKTOP is not set +# CONFIG_EXTRA_COMPAT is not set +CONFIG_INCLUDE_SUSv2=y +# CONFIG_USE_PORTABLE_CODE is not set +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_LOCALE_SUPPORT=y +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_LONG_OPTS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +CONFIG_FEATURE_UTMP=y +CONFIG_FEATURE_WTMP=y +CONFIG_FEATURE_PIDFILE=y +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 + +# +# 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=y +CONFIG_CROSS_COMPILER_PREFIX="" +CONFIG_EXTRA_CFLAGS="" + +# +# 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 +# +# CONFIG_INSTALL_NO_USR is not set +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_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +CONFIG_FEATURE_FAST_TOP=y +# CONFIG_FEATURE_ETC_NETWORKS is not set +CONFIG_FEATURE_EDITING=y +CONFIG_FEATURE_EDITING_MAX_LEN=1024 +# CONFIG_FEATURE_EDITING_VI is not set +CONFIG_FEATURE_EDITING_HISTORY=15 +CONFIG_FEATURE_EDITING_SAVEHISTORY=y +CONFIG_FEATURE_TAB_COMPLETION=y +# CONFIG_FEATURE_USERNAME_COMPLETION is not set +# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set +# 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_MONOTONIC_SYSCALL is not set +CONFIG_IOCTL_HEX2STR_ERROR=y +# CONFIG_FEATURE_HWIB is not set + +# +# Applets +# + +# +# Archival Utilities +# +CONFIG_FEATURE_SEAMLESS_LZMA=y +CONFIG_FEATURE_SEAMLESS_BZ2=y +CONFIG_FEATURE_SEAMLESS_GZ=y +CONFIG_FEATURE_SEAMLESS_Z=y +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_FEATURE_AR_CREATE=y +CONFIG_BUNZIP2=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_GUNZIP=y +CONFIG_GZIP=y +CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LZOP=y +# CONFIG_LZOP_COMPR_HIGH is not set +CONFIG_RPM2CPIO=y +CONFIG_RPM=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_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_UNZIP=y + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAL=y +CONFIG_CAT=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_DATE=y +CONFIG_FEATURE_DATE_ISOFMT=y +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_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_HOSTID=y +CONFIG_ID=y +CONFIG_INSTALL=y +CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y +CONFIG_LN=y +CONFIG_LOGNAME=y +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_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_STAT=y +CONFIG_FEATURE_STAT_FORMAT=y +CONFIG_STTY=y +CONFIG_SUM=y +CONFIG_SYNC=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_FEATURE_TEST_64=y +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +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_WHO=y +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y + +# +# Console Utilities +# +CONFIG_CHVT=y +CONFIG_CLEAR=y +CONFIG_DEALLOCVT=y +CONFIG_DUMPKMAP=y +CONFIG_KBD_MODE=y +CONFIG_LOADFONT=y +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=y +CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y +CONFIG_DEFAULT_SETFONT_DIR="" +CONFIG_SETKEYCODES=y +CONFIG_SETLOGCONS=y +CONFIG_SHOWKEY=y + +# +# Common options for loadfont and setfont +# +# 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_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_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_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_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 + +# +# Init Utilities +# +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_INIT_QUIET=y +CONFIG_FEATURE_INIT_COREDUMPS=y +CONFIG_LINUXRC=y +CONFIG_HALT=y +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +CONFIG_MESG=y + +# +# Login/Password Management Utilities +# +CONFIG_FEATURE_SHADOWPASSWDS=y +CONFIG_USE_BB_PWD_GRP=y +CONFIG_USE_BB_SHADOW=y +CONFIG_USE_BB_CRYPT=y +CONFIG_USE_BB_CRYPT_SHA=y +CONFIG_ADDGROUP=y +CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y +CONFIG_FEATURE_ADDUSER_TO_GROUP=y +CONFIG_DELGROUP=y +CONFIG_FEATURE_DEL_USER_FROM_GROUP=y +# CONFIG_FEATURE_CHECK_NAMES is not set +CONFIG_ADDUSER=y +CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y +CONFIG_FIRST_SYSTEM_ID=100 +CONFIG_LAST_SYSTEM_ID=999 +CONFIG_DELUSER=y +CONFIG_GETTY=y +CONFIG_LOGIN=y +# CONFIG_PAM 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_CRYPTPW=y +CONFIG_CHPASSWD=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 + +# +# Linux Module Utilities +# +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_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_DEFAULT_MODULES_DIR="/lib/modules" +CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" + +# +# Linux System Utilities +# +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +CONFIG_BLKID=y +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_FDISK_ADVANCED=y +CONFIG_FINDFS=y +# CONFIG_FLOCK is not set +CONFIG_FREERAMDISK=y +CONFIG_FSCK_MINIX=y +# CONFIG_MKFS_EXT2 is not set +CONFIG_MKFS_MINIX=y + +# +# Minix filesystem support +# +CONFIG_FEATURE_MINIX2=y +# 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=y +CONFIG_IPCRM=y +CONFIG_IPCS=y +CONFIG_LOSETUP=y +CONFIG_LSPCI=y +CONFIG_LSUSB=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_MKSWAP=y +CONFIG_FEATURE_MKSWAP_UUID=y +CONFIG_MORE=y +CONFIG_VOLUMEID=y +CONFIG_FEATURE_VOLUMEID_EXT=y +CONFIG_FEATURE_VOLUMEID_BTRFS=y +CONFIG_FEATURE_VOLUMEID_REISERFS=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_LINUXSWAP=y +CONFIG_FEATURE_VOLUMEID_CRAMFS=y +CONFIG_FEATURE_VOLUMEID_ROMFS=y +CONFIG_FEATURE_VOLUMEID_SYSV=y +CONFIG_FEATURE_VOLUMEID_OCFS2=y +CONFIG_FEATURE_VOLUMEID_LINUXRAID=y +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=y +CONFIG_FEATURE_MOUNT_CIFS=y +CONFIG_FEATURE_MOUNT_FLAGS=y +CONFIG_FEATURE_MOUNT_FSTAB=y +CONFIG_PIVOT_ROOT=y +CONFIG_RDATE=y +CONFIG_RDEV=y +CONFIG_READPROFILE=y +CONFIG_RTCWAKE=y +CONFIG_SCRIPT=y +CONFIG_SCRIPTREPLAY=y +CONFIG_SETARCH=y +CONFIG_SWAPONOFF=y +CONFIG_FEATURE_SWAPON_PRI=y +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 is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set + +# +# Miscellaneous Utilities +# +CONFIG_ADJTIMEX=y +# CONFIG_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_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 +# 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=y +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 is not set +# CONFIG_INOTIFYD is not set +CONFIG_LAST=y +# CONFIG_FEATURE_LAST_SMALL is not set +CONFIG_FEATURE_LAST_FANCY=y +CONFIG_LESS=y +CONFIG_FEATURE_LESS_MAXLINES=9999999 +CONFIG_FEATURE_LESS_BRACKETS=y +CONFIG_FEATURE_LESS_FLAGS=y +CONFIG_FEATURE_LESS_MARKS=y +CONFIG_FEATURE_LESS_REGEXP=y +CONFIG_FEATURE_LESS_WINCH=y +CONFIG_FEATURE_LESS_DASHCMD=y +CONFIG_FEATURE_LESS_LINENUMS=y +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=y +CONFIG_RAIDAUTORUN=y +# CONFIG_READAHEAD is not set +CONFIG_RUNLEVEL=y +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=y +# CONFIG_WATCHDOG is not set + +# +# Networking Utilities +# +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=y +CONFIG_ARPING=y +CONFIG_BRCTL=y +CONFIG_FEATURE_BRCTL_FANCY=y +CONFIG_FEATURE_BRCTL_SHOW=y +CONFIG_DNSD=y +CONFIG_ETHER_WAKE=y +CONFIG_FAKEIDENTD=y +CONFIG_FTPD=y +CONFIG_FEATURE_FTP_WRITE=y +CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=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_USE_SENDFILE=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_IFCONFIG=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +CONFIG_FEATURE_IFCONFIG_SLIP=y +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y +# 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=y +CONFIG_IP=y +CONFIG_FEATURE_IP_ADDRESS=y +CONFIG_FEATURE_IP_LINK=y +CONFIG_FEATURE_IP_ROUTE=y +CONFIG_FEATURE_IP_TUNNEL=y +CONFIG_FEATURE_IP_RULE=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_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y +CONFIG_NAMEIF=y +CONFIG_FEATURE_NAMEIF_EXTENDED=y +CONFIG_NC=y +CONFIG_NC_SERVER=y +CONFIG_NC_EXTRA=y +CONFIG_NETSTAT=y +CONFIG_FEATURE_NETSTAT_WIDE=y +CONFIG_FEATURE_NETSTAT_PRG=y +CONFIG_NSLOOKUP=y +CONFIG_NTPD=y +CONFIG_FEATURE_NTPD_SERVER=y +CONFIG_PING=y +CONFIG_PING6=y +CONFIG_FEATURE_FANCY_PING=y +CONFIG_PSCAN=y +CONFIG_ROUTE=y +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 +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_UDHCPD=y +CONFIG_DHCPRELAY=y +CONFIG_DUMPLEASES=y +CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y +CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases" +CONFIG_UDHCPC=y +CONFIG_FEATURE_UDHCPC_ARPING=y +CONFIG_FEATURE_UDHCP_PORT=y +CONFIG_UDHCP_DEBUG=9 +CONFIG_FEATURE_UDHCP_RFC3397=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_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=y + +# +# 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_FREE=y +CONFIG_FUSER=y +CONFIG_KILL=y +CONFIG_KILLALL=y +CONFIG_KILLALL5=y +CONFIG_NMETER=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=y +# CONFIG_FEATURE_PS_TIME is not set +# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set +# 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_UPTIME=y +CONFIG_WATCH=y + +# +# Runit Utilities +# +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_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=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_FEATURE_SH_IS_ASH=y +# CONFIG_FEATURE_SH_IS_HUSH is not set +# CONFIG_FEATURE_SH_IS_NONE is not set +CONFIG_ASH=y +CONFIG_ASH_BASH_COMPAT=y +CONFIG_ASH_JOB_CONTROL=y +CONFIG_ASH_ALIAS=y +CONFIG_ASH_GETOPTS=y +CONFIG_ASH_ECHO=y +CONFIG_ASH_PRINTF=y +CONFIG_ASH_TEST=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_HUSH=y +CONFIG_HUSH_BASH_COMPAT=y +CONFIG_HUSH_HELP=y +CONFIG_HUSH_INTERACTIVE=y +CONFIG_HUSH_JOB=y +CONFIG_HUSH_TICK=y +CONFIG_HUSH_IF=y +CONFIG_HUSH_LOOPS=y +CONFIG_HUSH_CASE=y +CONFIG_HUSH_FUNCTIONS=y +CONFIG_HUSH_LOCAL=y +CONFIG_HUSH_EXPORT_N=y +CONFIG_HUSH_RANDOM_SUPPORT=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_CTTYHACK=y + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +CONFIG_FEATURE_ROTATE_LOGFILE=y +CONFIG_FEATURE_REMOTE_LOG=y +CONFIG_FEATURE_SYSLOGD_DUP=y +CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256 +CONFIG_FEATURE_IPC_SYSLOG=y +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 +CONFIG_LOGREAD=y +CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y +CONFIG_KLOGD=y +CONFIG_LOGGER=y diff --git a/busybox/configs/android2_defconfig b/busybox/configs/android2_defconfig new file mode 100644 index 00000000000000..03323654da8def --- /dev/null +++ b/busybox/configs/android2_defconfig @@ -0,0 +1,982 @@ +# Run "make android2_defconfig", then "make". +# +# Tested with the standalone toolchain from ndk r6: +# android-ndk-r6/build/tools/make-standalone-toolchain.sh --platform=android-8 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +# CONFIG_DESKTOP is not set +# 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 is not set +# CONFIG_FEATURE_VERBOSE_USAGE is not set +# CONFIG_FEATURE_COMPRESS_USAGE is not set +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_INSTALL_NO_USR is not set +# CONFIG_LOCALE_SUPPORT is not set +# CONFIG_UNICODE_SUPPORT is not set +# CONFIG_UNICODE_USING_LOCALE is not set +# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set +CONFIG_SUBST_WCHAR=0 +CONFIG_LAST_SUPPORTED_WCHAR=0 +# 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_LONG_OPTS is not set +# 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_FEATURE_SUID is not set +# CONFIG_FEATURE_SUID_CONFIG is not set +# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y + +# +# 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="arm-linux-androideabi-" +CONFIG_EXTRA_CFLAGS="" + +# +# 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 +# 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 is not set +CONFIG_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +# CONFIG_FEATURE_FAST_TOP is not set +# CONFIG_FEATURE_ETC_NETWORKS is not set +# CONFIG_FEATURE_EDITING is not set +CONFIG_FEATURE_EDITING_MAX_LEN=0 +# CONFIG_FEATURE_EDITING_VI is not set +CONFIG_FEATURE_EDITING_HISTORY=0 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +# CONFIG_FEATURE_TAB_COMPLETION is not set +# CONFIG_FEATURE_USERNAME_COMPLETION is not set +# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set +# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set +# CONFIG_FEATURE_NON_POSIX_CP is not set +# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set +CONFIG_FEATURE_COPYBUF_KB=4 +# CONFIG_FEATURE_SKIP_ROOTFS is not set +# CONFIG_MONOTONIC_SYSCALL is not set +# CONFIG_IOCTL_HEX2STR_ERROR is not set +# CONFIG_FEATURE_HWIB is not set + +# +# 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=y +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_FEATURE_AR_CREATE=y +CONFIG_BUNZIP2=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_GUNZIP=y +CONFIG_GZIP=y +# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set +CONFIG_LZOP=y +CONFIG_LZOP_COMPR_HIGH=y +CONFIG_RPM2CPIO=y +CONFIG_RPM=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 is not set +# CONFIG_FEATURE_TAR_TO_COMMAND is not set +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 + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAT=y +# CONFIG_DATE is not set +# CONFIG_FEATURE_DATE_ISOFMT is not set +# CONFIG_FEATURE_DATE_NANO is not set +# CONFIG_FEATURE_DATE_COMPAT is not set +# CONFIG_ID is not set +# CONFIG_GROUPS is not set +CONFIG_TEST=y +CONFIG_FEATURE_TEST_64=y +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +CONFIG_BASE64=y +CONFIG_CAL=y +CONFIG_CATV=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set +CONFIG_CHROOT=y +CONFIG_CKSUM=y +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 +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 is not set +# CONFIG_FEATURE_ENV_LONG_OPTIONS is not set +CONFIG_EXPAND=y +# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set +# CONFIG_EXPR is not set +# CONFIG_EXPR_MATH_SUPPORT_64 is not set +CONFIG_FALSE=y +CONFIG_FOLD=y +# CONFIG_FSYNC is not set +CONFIG_HEAD=y +CONFIG_FEATURE_FANCY_HEAD=y +# CONFIG_HOSTID is not set +CONFIG_INSTALL=y +# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set +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 is not set +# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +# CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set +CONFIG_MKFIFO=y +CONFIG_MKNOD=y +CONFIG_MV=y +# CONFIG_FEATURE_MV_LONG_OPTIONS is not set +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 is not set +CONFIG_SEQ=y +CONFIG_SHA1SUM=y +CONFIG_SHA256SUM=y +CONFIG_SHA512SUM=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_STAT is not set +# 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 +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TRUE=y +# CONFIG_TTY is not set +CONFIG_UNAME=y +CONFIG_UNEXPAND=y +# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set +CONFIG_UNIQ=y +# CONFIG_USLEEP is not set +CONFIG_UUDECODE=y +CONFIG_UUENCODE=y +CONFIG_WC=y +CONFIG_FEATURE_WC_LARGE=y +# CONFIG_WHO is not set +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +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 is not set +# 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 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=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 is not set +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 is not set +CONFIG_WHICH=y + +# +# Editors +# +CONFIG_PATCH=y +# CONFIG_VI is not set +CONFIG_FEATURE_VI_MAX_LEN=0 +# CONFIG_FEATURE_VI_8BIT is not set +# CONFIG_FEATURE_VI_COLON is not set +# CONFIG_FEATURE_VI_YANKMARK is not set +# CONFIG_FEATURE_VI_SEARCH is not set +# CONFIG_FEATURE_VI_REGEX_SEARCH is not set +# CONFIG_FEATURE_VI_USE_SIGNALS is not set +# CONFIG_FEATURE_VI_DOT_CMD is not set +# CONFIG_FEATURE_VI_READONLY is not set +# CONFIG_FEATURE_VI_SETOPTS is not set +# CONFIG_FEATURE_VI_SET is not set +# CONFIG_FEATURE_VI_WIN_RESIZE is not set +# CONFIG_FEATURE_VI_ASK_TERMINAL is not set +# CONFIG_AWK is not set +# CONFIG_FEATURE_AWK_LIBM is not set +CONFIG_CMP=y +CONFIG_DIFF=y +# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set +CONFIG_FEATURE_DIFF_DIR=y +# CONFIG_ED is not set +# CONFIG_SED is not set +# CONFIG_FEATURE_ALLOW_EXEC is not set + +# +# Finding Utilities +# +# CONFIG_FIND is not set +# CONFIG_FEATURE_FIND_PRINT0 is not set +# CONFIG_FEATURE_FIND_MTIME is not set +# CONFIG_FEATURE_FIND_MMIN is not set +# CONFIG_FEATURE_FIND_PERM is not set +# CONFIG_FEATURE_FIND_TYPE is not set +# CONFIG_FEATURE_FIND_XDEV is not set +# CONFIG_FEATURE_FIND_MAXDEPTH is not set +# CONFIG_FEATURE_FIND_NEWER is not set +# CONFIG_FEATURE_FIND_INUM is not set +# CONFIG_FEATURE_FIND_EXEC is not set +# CONFIG_FEATURE_FIND_USER is not set +# CONFIG_FEATURE_FIND_GROUP is not set +# CONFIG_FEATURE_FIND_NOT is not set +# CONFIG_FEATURE_FIND_DEPTH is not set +# CONFIG_FEATURE_FIND_PAREN is not set +# CONFIG_FEATURE_FIND_SIZE is not set +# CONFIG_FEATURE_FIND_PRUNE is not set +# CONFIG_FEATURE_FIND_DELETE is not set +# CONFIG_FEATURE_FIND_PATH is not set +# CONFIG_FEATURE_FIND_REGEX is not set +# CONFIG_FEATURE_FIND_CONTEXT is not set +# CONFIG_FEATURE_FIND_LINKS is not set +# CONFIG_GREP is not set +# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set +# CONFIG_FEATURE_GREP_CONTEXT is not set +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 + +# +# Init Utilities +# +# CONFIG_BOOTCHARTD is not set +# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set +# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set +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_INIT_QUIET=y +CONFIG_FEATURE_INIT_COREDUMPS=y +CONFIG_LINUXRC=y +CONFIG_INIT_TERMINAL_TYPE="linux" +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 is not set +# CONFIG_USE_BB_CRYPT_SHA is not set +# CONFIG_ADDUSER is not set +# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set +# CONFIG_FEATURE_CHECK_NAMES is not set +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_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_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_SU is not set +# CONFIG_FEATURE_SU_SYSLOG is not set +# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Linux Ext2 FS Progs +# +CONFIG_CHATTR=y +# CONFIG_FSCK is not set +CONFIG_LSATTR=y +CONFIG_TUNE2FS=y + +# +# 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_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_DEFAULT_MODULES_DIR="/lib/modules" +CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" + +# +# Linux System Utilities +# +CONFIG_BLOCKDEV=y +CONFIG_REV=y +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +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 is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_FEATURE_MINIX2 is not set +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +CONFIG_GETOPT=y +CONFIG_FEATURE_GETOPT_LONG=y +CONFIG_HEXDUMP=y +CONFIG_FEATURE_HEXDUMP_REVERSE=y +CONFIG_HD=y +# CONFIG_HWCLOCK is not set +# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set +# 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_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_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 +CONFIG_READPROFILE=y +CONFIG_RTCWAKE=y +CONFIG_SCRIPT=y +CONFIG_SCRIPTREPLAY=y +# CONFIG_SETARCH is not set +# CONFIG_SWAPONOFF is not set +# CONFIG_FEATURE_SWAPON_PRI is not set +# CONFIG_SWITCH_ROOT is not set +# CONFIG_UMOUNT is not set +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set +CONFIG_VOLUMEID=y + +# +# Filesystem/Volume identification +# +CONFIG_FEATURE_VOLUMEID_EXT=y +CONFIG_FEATURE_VOLUMEID_BTRFS=y +CONFIG_FEATURE_VOLUMEID_REISERFS=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_LINUXSWAP=y +CONFIG_FEATURE_VOLUMEID_CRAMFS=y +CONFIG_FEATURE_VOLUMEID_ROMFS=y +CONFIG_FEATURE_VOLUMEID_SYSV=y +CONFIG_FEATURE_VOLUMEID_OCFS2=y +CONFIG_FEATURE_VOLUMEID_LINUXRAID=y + +# +# Miscellaneous Utilities +# +# CONFIG_CONSPY is not set +# CONFIG_NANDWRITE is not set +CONFIG_NANDDUMP=y +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_ADJTIMEX is not set +# 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_CROND is not set +# CONFIG_FEATURE_CROND_D is not set +# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set +CONFIG_FEATURE_CROND_DIR="" +# CONFIG_CRONTAB is not set +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 is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +CONFIG_FBSPLASH=y +CONFIG_FLASHCP=y +CONFIG_FLASH_LOCK=y +CONFIG_FLASH_UNLOCK=y +# CONFIG_FLASH_ERASEALL is not set +# CONFIG_IONICE is not set +CONFIG_INOTIFYD=y +# CONFIG_LAST is not set +# CONFIG_FEATURE_LAST_SMALL is not set +# CONFIG_FEATURE_LAST_FANCY is not set +# CONFIG_LESS is not set +CONFIG_FEATURE_LESS_MAXLINES=0 +# CONFIG_FEATURE_LESS_BRACKETS is not set +# CONFIG_FEATURE_LESS_FLAGS is not set +# CONFIG_FEATURE_LESS_MARKS is not set +# CONFIG_FEATURE_LESS_REGEXP is not set +# CONFIG_FEATURE_LESS_WINCH is not set +# CONFIG_FEATURE_LESS_DASHCMD is not set +# CONFIG_FEATURE_LESS_LINENUMS 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 is not set +# CONFIG_MOUNTPOINT is not set +# 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 + +# +# 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 is not set +# CONFIG_PING6 is not set +# CONFIG_FEATURE_FANCY_PING is not set +CONFIG_WHOIS=y +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_FEATURE_UNIX_LOCAL is not set +# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +CONFIG_ARP=y +# CONFIG_ARPING is not set +# CONFIG_BRCTL is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +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_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 +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=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y +# CONFIG_IFENSLAVE is not set +# CONFIG_IFPLUGD is not set +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 is not set +CONFIG_FEATURE_IFUPDOWN_MAPPING=y +# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set +# CONFIG_INETD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set +# 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_TUNNEL=y +CONFIG_FEATURE_IP_RULE=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_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set +CONFIG_NETSTAT=y +CONFIG_FEATURE_NETSTAT_WIDE=y +CONFIG_FEATURE_NETSTAT_PRG=y +# CONFIG_NSLOOKUP is not set +# CONFIG_NTPD is not set +# CONFIG_FEATURE_NTPD_SERVER is not set +CONFIG_PSCAN=y +CONFIG_ROUTE=y +# CONFIG_SLATTACH is not set +CONFIG_TCPSVD=y +# CONFIG_TELNET is not set +# CONFIG_FEATURE_TELNET_TTYPE is not set +# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set +# CONFIG_TELNETD is not set +# CONFIG_FEATURE_TELNETD_STANDALONE is not set +# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set +# CONFIG_TFTP is not set +# CONFIG_TFTPD is not set +# CONFIG_FEATURE_TFTP_GET is not set +# CONFIG_FEATURE_TFTP_PUT is not set +# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set +# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set +# CONFIG_TFTP_DEBUG is not set +# CONFIG_TRACEROUTE is not set +# CONFIG_TRACEROUTE6 is not set +# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set +# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set +CONFIG_TUNCTL=y +CONFIG_FEATURE_TUNCTL_UG=y +# CONFIG_UDHCPD is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set +CONFIG_DHCPD_LEASES_FILE="" +CONFIG_UDHCPC=y +CONFIG_FEATURE_UDHCPC_ARPING=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 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 is not set +CONFIG_FEATURE_WGET_TIMEOUT=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_MPSTAT=y +CONFIG_NMETER=y +CONFIG_PMAP=y +CONFIG_POWERTOP=y +CONFIG_PSTREE=y +CONFIG_PWDX=y +CONFIG_SMEMCAP=y +# CONFIG_FREE is not set +CONFIG_FUSER=y +# CONFIG_KILL is not set +# CONFIG_KILLALL is not set +# CONFIG_KILLALL5 is not set +# CONFIG_PGREP is not set +CONFIG_PIDOF=y +CONFIG_FEATURE_PIDOF_SINGLE=y +CONFIG_FEATURE_PIDOF_OMIT=y +# CONFIG_PKILL is not set +# CONFIG_PS is not set +# CONFIG_FEATURE_PS_WIDE is not set +# CONFIG_FEATURE_PS_TIME is not set +# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set +# 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_UPTIME is not set +CONFIG_WATCH=y + +# +# Runit Utilities +# +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_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=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 is not set +# CONFIG_ASH_BASH_COMPAT is not set +# CONFIG_ASH_IDLE_TIMEOUT is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_ECHO is not set +# CONFIG_ASH_PRINTF is not set +# CONFIG_ASH_TEST is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +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_FEATURE_SH_IS_ASH is not set +# CONFIG_FEATURE_SH_IS_HUSH is not set +CONFIG_FEATURE_SH_IS_NONE=y +# 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 is not set +# CONFIG_SH_MATH_SUPPORT_64 is not set +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set +# CONFIG_FEATURE_SH_HISTFILESIZE is not set + +# +# System Logging Utilities +# +# 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_LOGREAD is not set +# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set +CONFIG_KLOGD=y +CONFIG_FEATURE_KLOGD_KLOGCTL=y +# CONFIG_LOGGER is not set diff --git a/busybox/configs/android_502_defconfig b/busybox/configs/android_502_defconfig new file mode 100644 index 00000000000000..6b640bfb768ec8 --- /dev/null +++ b/busybox/configs/android_502_defconfig @@ -0,0 +1,1129 @@ +## 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 + +# +# 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_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 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_INIT_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_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_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_ECHO=y +CONFIG_ASH_PRINTF=y +CONFIG_ASH_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_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/busybox/configs/android_defconfig b/busybox/configs/android_defconfig new file mode 100644 index 00000000000000..3b34f37aaea9a9 --- /dev/null +++ b/busybox/configs/android_defconfig @@ -0,0 +1,1014 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.20.0.git +# Sun Nov 6 07:51:38 2011 +# +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 is not set +# CONFIG_FEATURE_VERBOSE_USAGE is not set +# CONFIG_FEATURE_COMPRESS_USAGE is not set +# CONFIG_FEATURE_INSTALLER is not set +# CONFIG_INSTALL_NO_USR is not set +# CONFIG_LOCALE_SUPPORT is not set +# CONFIG_UNICODE_SUPPORT is not set +# CONFIG_UNICODE_USING_LOCALE is not set +# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set +CONFIG_SUBST_WCHAR=0 +CONFIG_LAST_SUPPORTED_WCHAR=0 +# 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_LONG_OPTS is not set +# 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_FEATURE_SUID is not set +# CONFIG_FEATURE_SUID_CONFIG is not set +# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y + +# +# 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="arm-eabi-" +# +# Removed: +# warning flags: +# -Wno-multichar -W -Wall -Wno-unused -Winit-self -Wpointer-arith +# -Werror=return-type -Werror=non-virtual-dtor -Werror=address +# -Werror=sequence-point -Wstrict-aliasing=2 -Wno-undef -Wno-shadow +# bbox already adds these: +# -ffunction-sections -fomit-frame-pointer +# enabled implicitly by -Os: +# -frerun-cse-after-loop +# should be not needed, or even increases code size: +# -finline-functions -fno-inline-functions-called-once -finline-limit=64 +# -fstack-protector -fno-strict-aliasing -fno-exceptions -funwind-tables +# -fmessage-length=0 (only affects error message format) +# todo: do we need these? - +# -fno-short-enums +# -fgcse-after-reload +# -frename-registers +CONFIG_EXTRA_CFLAGS="-I$A/system/core/include -I$A/bionic/libc/arch-arm/include -I$A/bionic/libc/include -I$A/bionic/libc/kernel/common -I$A/bionic/libc/kernel/arch-arm -I$A/bionic/libm/include -I$A/bionic/libm/include/arch/arm -include $A/system/core/include/arch/linux-arm/AndroidConfig.h -I$A/system/core/include/arch/linux-arm/ -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" + +# +# 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 +# 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 is not set +CONFIG_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +# CONFIG_FEATURE_FAST_TOP is not set +# CONFIG_FEATURE_ETC_NETWORKS is not set +# CONFIG_FEATURE_EDITING is not set +CONFIG_FEATURE_EDITING_MAX_LEN=0 +# CONFIG_FEATURE_EDITING_VI is not set +CONFIG_FEATURE_EDITING_HISTORY=0 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set +# CONFIG_FEATURE_REVERSE_SEARCH is not set +# CONFIG_FEATURE_TAB_COMPLETION is not set +# CONFIG_FEATURE_USERNAME_COMPLETION is not set +# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set +# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set +# CONFIG_FEATURE_NON_POSIX_CP is not set +# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set +CONFIG_FEATURE_COPYBUF_KB=4 +# CONFIG_FEATURE_SKIP_ROOTFS is not set +# CONFIG_MONOTONIC_SYSCALL is not set +# CONFIG_IOCTL_HEX2STR_ERROR is not set +# CONFIG_FEATURE_HWIB is not set + +# +# 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=y +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_FEATURE_AR_CREATE=y +CONFIG_BUNZIP2=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_GUNZIP=y +CONFIG_GZIP=y +# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set +CONFIG_GZIP_FAST=0 +CONFIG_LZOP=y +CONFIG_LZOP_COMPR_HIGH=y +CONFIG_RPM2CPIO=y +CONFIG_RPM=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 is not set +# CONFIG_FEATURE_TAR_TO_COMMAND is not set +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 + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAT=y +# CONFIG_DATE is not set +# CONFIG_FEATURE_DATE_ISOFMT is not set +# CONFIG_FEATURE_DATE_NANO is not set +# CONFIG_FEATURE_DATE_COMPAT is not set +# CONFIG_HOSTID is not set +# CONFIG_ID is not set +# CONFIG_GROUPS is not set +CONFIG_TEST=y +CONFIG_FEATURE_TEST_64=y +CONFIG_TOUCH=y +CONFIG_FEATURE_TOUCH_SUSV3=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=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 is not set +CONFIG_CHROOT=y +CONFIG_CKSUM=y +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 +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 is not set +CONFIG_EXPAND=y +# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set +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 is not set +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 is not set +# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +# CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set +CONFIG_MKFIFO=y +CONFIG_MKNOD=y +CONFIG_MV=y +# CONFIG_FEATURE_MV_LONG_OPTIONS is not set +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 is not set +CONFIG_SEQ=y +CONFIG_SHA1SUM=y +CONFIG_SHA256SUM=y +CONFIG_SHA512SUM=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_STAT is not set +# 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 +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TRUE=y +# CONFIG_TTY is not set +CONFIG_UNAME=y +CONFIG_UNEXPAND=y +# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set +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 for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +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 is not set +# 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 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=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 is not set +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 is not set +CONFIG_WHICH=y + +# +# Editors +# +CONFIG_PATCH=y +CONFIG_VI=y +CONFIG_FEATURE_VI_MAX_LEN=4096 +CONFIG_FEATURE_VI_8BIT=y +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_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_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_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 + +# +# 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_INIT_QUIET=y +CONFIG_FEATURE_INIT_COREDUMPS=y +CONFIG_LINUXRC=y +CONFIG_INIT_TERMINAL_TYPE="linux" +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 is not set +# CONFIG_USE_BB_CRYPT_SHA is not set +# CONFIG_ADDUSER is not set +# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set +# CONFIG_FEATURE_CHECK_NAMES is not set +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_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_SU is not set +# CONFIG_FEATURE_SU_SYSLOG is not set +# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Linux Ext2 FS Progs +# +CONFIG_CHATTR=y +# CONFIG_FSCK is not set +CONFIG_LSATTR=y +CONFIG_TUNE2FS=y + +# +# 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_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_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_REV=y +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +CONFIG_BLKID=y +CONFIG_FEATURE_BLKID_TYPE=y +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 is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_FEATURE_MINIX2 is not set +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +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 is not set +# 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_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 +CONFIG_READPROFILE=y +CONFIG_RTCWAKE=y +CONFIG_SCRIPT=y +CONFIG_SCRIPTREPLAY=y +# CONFIG_SETARCH is not set +# CONFIG_SWAPONOFF is not set +# CONFIG_FEATURE_SWAPON_PRI is not set +CONFIG_SWITCH_ROOT=y +# CONFIG_UMOUNT is not set +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set +CONFIG_VOLUMEID=y + +# +# Filesystem/Volume identification +# +CONFIG_FEATURE_VOLUMEID_EXT=y +CONFIG_FEATURE_VOLUMEID_BTRFS=y +CONFIG_FEATURE_VOLUMEID_REISERFS=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_LINUXSWAP=y +CONFIG_FEATURE_VOLUMEID_CRAMFS=y +CONFIG_FEATURE_VOLUMEID_ROMFS=y +CONFIG_FEATURE_VOLUMEID_SYSV=y +CONFIG_FEATURE_VOLUMEID_OCFS2=y +CONFIG_FEATURE_VOLUMEID_LINUXRAID=y + +# +# Miscellaneous Utilities +# +# CONFIG_CONSPY is not set +CONFIG_LESS=y +CONFIG_FEATURE_LESS_MAXLINES=9999999 +CONFIG_FEATURE_LESS_BRACKETS=y +CONFIG_FEATURE_LESS_FLAGS=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=y +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_ADJTIMEX is not set +# 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_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 +# 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 is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +CONFIG_FBSPLASH=y +CONFIG_FLASHCP=y +CONFIG_FLASH_LOCK=y +CONFIG_FLASH_UNLOCK=y +# CONFIG_FLASH_ERASEALL is not set +# CONFIG_IONICE is not set +CONFIG_INOTIFYD=y +# CONFIG_LAST is not set +# CONFIG_FEATURE_LAST_SMALL 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 is not set +# CONFIG_MOUNTPOINT is not set +# 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 + +# +# 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=y +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_FEATURE_UNIX_LOCAL is not set +# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +CONFIG_ARP=y +# CONFIG_ARPING is not set +# CONFIG_BRCTL is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +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_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 +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=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y +# CONFIG_IFENSLAVE is not set +# CONFIG_IFPLUGD is not set +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 is not set +CONFIG_FEATURE_IFUPDOWN_MAPPING=y +CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP=y +# CONFIG_INETD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set +# 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_TUNNEL=y +CONFIG_FEATURE_IP_RULE=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_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set +CONFIG_NETSTAT=y +CONFIG_FEATURE_NETSTAT_WIDE=y +CONFIG_FEATURE_NETSTAT_PRG=y +# CONFIG_NSLOOKUP is not set +# CONFIG_NTPD is not set +# CONFIG_FEATURE_NTPD_SERVER is not set +CONFIG_PSCAN=y +CONFIG_ROUTE=y +# CONFIG_SLATTACH is not set +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 is not set +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 is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set +CONFIG_DHCPD_LEASES_FILE="" +CONFIG_UDHCPC=y +CONFIG_FEATURE_UDHCPC_ARPING=y +CONFIG_FEATURE_UDHCP_PORT=y +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_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set +CONFIG_FEATURE_WGET_TIMEOUT=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_MPSTAT=y +CONFIG_NMETER=y +CONFIG_PMAP=y +CONFIG_POWERTOP=y +CONFIG_PSTREE=y +CONFIG_PWDX=y +CONFIG_SMEMCAP=y +CONFIG_UPTIME=y +# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set +CONFIG_FREE=y +CONFIG_FUSER=y +# CONFIG_KILL is not set +# CONFIG_KILLALL is not set +# CONFIG_KILLALL5 is not set +# CONFIG_PGREP is not set +CONFIG_PIDOF=y +CONFIG_FEATURE_PIDOF_SINGLE=y +CONFIG_FEATURE_PIDOF_OMIT=y +# CONFIG_PKILL 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_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 + +# +# Runit Utilities +# +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_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=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 is not set +# CONFIG_ASH_BASH_COMPAT is not set +# CONFIG_ASH_IDLE_TIMEOUT is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_ECHO is not set +# CONFIG_ASH_PRINTF is not set +# CONFIG_ASH_TEST is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +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_FEATURE_SH_IS_ASH is not set +# CONFIG_FEATURE_SH_IS_HUSH is not set +CONFIG_FEATURE_SH_IS_NONE=y +# 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 is not set +# CONFIG_SH_MATH_SUPPORT_64 is not set +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set +# CONFIG_FEATURE_SH_HISTFILESIZE is not set + +# +# System Logging Utilities +# +# 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_LOGREAD is not set +# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set +CONFIG_KLOGD=y +CONFIG_FEATURE_KLOGD_KLOGCTL=y +# CONFIG_LOGGER is not set diff --git a/busybox/configs/android_ndk_defconfig b/busybox/configs/android_ndk_defconfig new file mode 100644 index 00000000000000..7f65d544c5adaa --- /dev/null +++ b/busybox/configs/android_ndk_defconfig @@ -0,0 +1,1044 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.24.0.git +# Fri Sep 18 20:39:29 2015 +# +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_FEATURE_INSTALLER=y +CONFIG_INSTALL_NO_USR=y +# CONFIG_LOCALE_SUPPORT is not set +# CONFIG_UNICODE_SUPPORT is not set +# CONFIG_UNICODE_USING_LOCALE is not set +# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set +CONFIG_SUBST_WCHAR=0 +CONFIG_LAST_SUPPORTED_WCHAR=0 +# 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 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 +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y + +# +# 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="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 -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" + +# +# Debugging Options +# +# 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 +# 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 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_EDITING is not set +CONFIG_FEATURE_EDITING_MAX_LEN=0 +# CONFIG_FEATURE_EDITING_VI is not set +CONFIG_FEATURE_EDITING_HISTORY=0 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set +# CONFIG_FEATURE_REVERSE_SEARCH is not set +# CONFIG_FEATURE_TAB_COMPLETION is not set +# CONFIG_FEATURE_USERNAME_COMPLETION is not set +# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set +# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set +# CONFIG_FEATURE_NON_POSIX_CP is not set +CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y +CONFIG_FEATURE_COPYBUF_KB=4 +# CONFIG_FEATURE_SKIP_ROOTFS is not set +# CONFIG_MONOTONIC_SYSCALL is not set +# CONFIG_IOCTL_HEX2STR_ERROR is not set +# CONFIG_FEATURE_HWIB is not set + +# +# 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=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_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_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 is not set +# CONFIG_FEATURE_TAR_TO_COMMAND is not set +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 is not set +# 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 +CONFIG_CAL=y +CONFIG_CATV=y +CONFIG_CHGRP=y +CONFIG_CHMOD=y +CONFIG_CHOWN=y +# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set +CONFIG_CHROOT=y +CONFIG_CKSUM=y +CONFIG_COMM=y +CONFIG_CP=y +# CONFIG_FEATURE_CP_LONG_OPTIONS is not set +CONFIG_CUT=y +# CONFIG_DF is not set +# CONFIG_FEATURE_DF_FANCY is not set +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 is not set +CONFIG_EXPAND=y +# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set +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 is not set +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 is not set +CONFIG_MD5SUM=y +CONFIG_MKDIR=y +# CONFIG_FEATURE_MKDIR_LONG_OPTIONS is not set +CONFIG_MKFIFO=y +CONFIG_MKNOD=y +CONFIG_MV=y +# CONFIG_FEATURE_MV_LONG_OPTIONS is not set +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 is not set +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_STAT is not set +# CONFIG_FEATURE_STAT_FORMAT is not set +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 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 +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 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 is not set +# 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 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=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 is not set +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 is not set +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 +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_INIT_QUIET=y +CONFIG_FEATURE_INIT_COREDUMPS=y +CONFIG_LINUXRC=y +CONFIG_INIT_TERMINAL_TYPE="linux" +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_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_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_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 +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Linux Ext2 FS Progs +# +CONFIG_CHATTR=y +# CONFIG_FSCK is not set +CONFIG_LSATTR=y +CONFIG_TUNE2FS=y + +# +# 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_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_DEFAULT_MODULES_DIR="/system/lib/modules" +CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" + +# +# Linux System Utilities +# +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 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 +CONFIG_FEATURE_BLKID_TYPE=y +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 is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_FEATURE_MINIX2 is not set +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +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 is not set +# 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 is not set +# CONFIG_RDATE is not set +CONFIG_RDEV=y +CONFIG_READPROFILE=y +CONFIG_RTCWAKE=y +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 +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set +CONFIG_VOLUMEID=y + +# +# Filesystem/Volume identification +# +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=y +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=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 +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 is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +CONFIG_FBSPLASH=y +CONFIG_FLASHCP=y +CONFIG_FLASH_LOCK=y +CONFIG_FLASH_UNLOCK=y +# CONFIG_FLASH_ERASEALL is not set +# CONFIG_IONICE is not set +CONFIG_INOTIFYD=y +# CONFIG_LAST is not set +# CONFIG_FEATURE_LAST_SMALL 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 is not set +# CONFIG_MOUNTPOINT is not set +# 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 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=y +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_FEATURE_UNIX_LOCAL is not set +# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +CONFIG_ARP=y +# CONFIG_ARPING is not set +# CONFIG_BRCTL is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +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 is not set +# CONFIG_HOSTNAME is not set +CONFIG_HTTPD=y +CONFIG_FEATURE_HTTPD_RANGES=y +CONFIG_FEATURE_HTTPD_SETUID=y +CONFIG_FEATURE_HTTPD_BASIC_AUTH=y +# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set +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=y +CONFIG_FEATURE_IFCONFIG_STATUS=y +# CONFIG_FEATURE_IFCONFIG_SLIP is not set +CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y +CONFIG_FEATURE_IFCONFIG_HW=y +CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y +# CONFIG_IFENSLAVE is not set +# CONFIG_IFPLUGD is not set +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 is not set +CONFIG_FEATURE_IFUPDOWN_MAPPING=y +CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP=y +# CONFIG_INETD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set +# 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_TUNNEL=y +CONFIG_FEATURE_IP_RULE=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_IPCALC=y +CONFIG_FEATURE_IPCALC_FANCY=y +# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set +CONFIG_NETSTAT=y +CONFIG_FEATURE_NETSTAT_WIDE=y +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 +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 is not set +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 is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set +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 +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_WGET=y +CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_AUTHENTICATION=y +# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set +CONFIG_FEATURE_WGET_TIMEOUT=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 is not set +# CONFIG_KILLALL is not set +# CONFIG_KILLALL5 is not set +# CONFIG_PGREP is not set +CONFIG_PIDOF=y +CONFIG_FEATURE_PIDOF_SINGLE=y +CONFIG_FEATURE_PIDOF_OMIT=y +# CONFIG_PKILL 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_RENICE=y +CONFIG_BB_SYSCTL=y +CONFIG_FEATURE_SHOW_THREADS=y +CONFIG_WATCH=y + +# +# Runit Utilities +# +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_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=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 is not set +# CONFIG_ASH_BASH_COMPAT is not set +# CONFIG_ASH_IDLE_TIMEOUT is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_ECHO is not set +# CONFIG_ASH_PRINTF is not set +# CONFIG_ASH_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 +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +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_FEATURE_SH_IS_ASH is not set +# CONFIG_FEATURE_SH_IS_HUSH is not set +CONFIG_FEATURE_SH_IS_NONE=y +# 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 is not set +# CONFIG_SH_MATH_SUPPORT_64 is not set +# CONFIG_FEATURE_SH_EXTRA_QUIET is not set +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set +# CONFIG_FEATURE_SH_HISTFILESIZE is not set + +# +# System Logging Utilities +# +# 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_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/busybox/configs/cygwin_defconfig b/busybox/configs/cygwin_defconfig new file mode 100644 index 00000000000000..ee370a61d327fd --- /dev/null +++ b/busybox/configs/cygwin_defconfig @@ -0,0 +1,985 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.19.0.git +# Sun Jul 10 12:48:50 2011 +# +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=65533 +CONFIG_LAST_SUPPORTED_WCHAR=0 +# 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_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_FEATURE_SUID=y +# CONFIG_FEATURE_SUID_CONFIG is not set +# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set +# CONFIG_SELINUX is not set +# CONFIG_FEATURE_PREFER_APPLETS is not set +CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" +CONFIG_FEATURE_SYSLOG=y + +# +# 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=y +CONFIG_CROSS_COMPILER_PREFIX="" +CONFIG_EXTRA_CFLAGS="" + +# +# 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 +# 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_FEATURE_FAST_TOP=y +# CONFIG_FEATURE_ETC_NETWORKS is not set +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_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 is not set +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_BUNZIP2=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_GUNZIP=y +CONFIG_GZIP=y +CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LZOP=y +# CONFIG_LZOP_COMPR_HIGH is not set +CONFIG_RPM2CPIO=y +CONFIG_RPM=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_UNCOMPRESS is not set +CONFIG_UNLZMA=y +CONFIG_FEATURE_LZMA_FAST=y +CONFIG_LZMA=y +CONFIG_UNXZ=y +CONFIG_XZ=y +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_ID=y +CONFIG_GROUPS=y +CONFIG_TEST=y +CONFIG_FEATURE_TEST_64=y +CONFIG_TOUCH=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +CONFIG_BASE64=y +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_DD=y +CONFIG_FEATURE_DD_SIGNAL_HANDLING=y +CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y +CONFIG_FEATURE_DD_IBS_OBS=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 is not set +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_HOSTID=y +CONFIG_INSTALL=y +CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y +CONFIG_LN=y +CONFIG_LOGNAME=y +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_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_STAT is not set +# 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 +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +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_WHO is not set +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y + +# +# Console Utilities +# +# CONFIG_CHVT is not set +# CONFIG_FGCONSOLE is not set +CONFIG_CLEAR=y +# CONFIG_DEALLOCVT is not set +# CONFIG_DUMPKMAP is not set +# CONFIG_KBD_MODE is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +# CONFIG_OPENVT is not set +CONFIG_RESET=y +CONFIG_RESIZE=y +CONFIG_FEATURE_RESIZE_PRINT=y +# CONFIG_SETCONSOLE is not set +# 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 is not set +# 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 +# +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_PATCH=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_AWK=y +CONFIG_FEATURE_AWK_LIBM=y +CONFIG_CMP=y +CONFIG_DIFF=y +CONFIG_FEATURE_DIFF_LONG_OPTIONS=y +CONFIG_FEATURE_DIFF_DIR=y +CONFIG_ED=y +CONFIG_SED=y +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_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 + +# +# Init Utilities +# +# CONFIG_BOOTCHARTD is not set +# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set +# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set +# CONFIG_HALT is not set +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +# CONFIG_INIT is not set +# CONFIG_FEATURE_USE_INITTAB is not set +# CONFIG_FEATURE_KILL_REMOVED is not set +CONFIG_FEATURE_KILL_DELAY=0 +# CONFIG_FEATURE_INIT_SCTTY is not set +# CONFIG_FEATURE_INIT_SYSLOG is not set +# CONFIG_FEATURE_INIT_QUIET is not set +# CONFIG_FEATURE_INIT_COREDUMPS is not set +# CONFIG_LINUXRC is not set +CONFIG_INIT_TERMINAL_TYPE="" +CONFIG_MESG=y +CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y + +# +# Login/Password Management Utilities +# +CONFIG_ADD_SHELL=y +CONFIG_REMOVE_SHELL=y +CONFIG_FEATURE_SHADOWPASSWDS=y +CONFIG_USE_BB_PWD_GRP=y +CONFIG_USE_BB_SHADOW=y +CONFIG_USE_BB_CRYPT=y +CONFIG_USE_BB_CRYPT_SHA=y +CONFIG_ADDUSER=y +CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y +# CONFIG_FEATURE_CHECK_NAMES is not set +CONFIG_FIRST_SYSTEM_ID=100 +CONFIG_LAST_SYSTEM_ID=999 +CONFIG_ADDGROUP=y +CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y +CONFIG_FEATURE_ADDUSER_TO_GROUP=y +CONFIG_DELUSER=y +CONFIG_DELGROUP=y +CONFIG_FEATURE_DEL_USER_FROM_GROUP=y +# CONFIG_GETTY is not set +CONFIG_LOGIN=y +# CONFIG_PAM 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_CRYPTPW=y +CONFIG_CHPASSWD=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 is not set +# CONFIG_LSATTR is not set +# CONFIG_TUNE2FS is not set + +# +# Linux Module Utilities +# +# CONFIG_MODINFO is not set +# CONFIG_MODPROBE_SMALL is not set +# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set +# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set +# 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="" +CONFIG_DEFAULT_DEPMOD_FILE="" + +# +# Linux System Utilities +# +# CONFIG_BLOCKDEV is not set +CONFIG_REV=y +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +# CONFIG_BLKID is not set +# CONFIG_FEATURE_BLKID_TYPE is not set +# CONFIG_DMESG is not set +# CONFIG_FEATURE_DMESG_PRETTY is not set +# CONFIG_FBSET is not set +# CONFIG_FEATURE_FBSET_FANCY is not set +# CONFIG_FEATURE_FBSET_READMODE is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set +# CONFIG_FEATURE_FDISK_WRITABLE is not set +# 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 is not set +# CONFIG_FINDFS is not set +CONFIG_FLOCK=y +# CONFIG_FREERAMDISK is not set +CONFIG_FSCK_MINIX=y +# CONFIG_MKFS_EXT2 is not set +# CONFIG_MKFS_MINIX is not set +CONFIG_FEATURE_MINIX2=y +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +CONFIG_GETOPT=y +CONFIG_FEATURE_GETOPT_LONG=y +CONFIG_HEXDUMP=y +CONFIG_FEATURE_HEXDUMP_REVERSE=y +CONFIG_HD=y +# CONFIG_HWCLOCK is not set +# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set +# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set +CONFIG_IPCRM=y +# CONFIG_IPCS is not set +# CONFIG_LOSETUP is not set +# CONFIG_LSPCI is not set +# CONFIG_LSUSB is not set +# 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_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 +CONFIG_READPROFILE=y +# CONFIG_RTCWAKE is not set +CONFIG_SCRIPT=y +CONFIG_SCRIPTREPLAY=y +# CONFIG_SETARCH is not set +# CONFIG_SWAPONOFF is not set +# CONFIG_FEATURE_SWAPON_PRI is not set +# CONFIG_SWITCH_ROOT is not set +# CONFIG_UMOUNT is not set +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set +# CONFIG_VOLUMEID is not set +# CONFIG_FEATURE_VOLUMEID_EXT is not set +# CONFIG_FEATURE_VOLUMEID_BTRFS is not set +# CONFIG_FEATURE_VOLUMEID_REISERFS is not set +# CONFIG_FEATURE_VOLUMEID_FAT is not set +# CONFIG_FEATURE_VOLUMEID_HFS is not set +# CONFIG_FEATURE_VOLUMEID_JFS is not set +# CONFIG_FEATURE_VOLUMEID_XFS is not set +# CONFIG_FEATURE_VOLUMEID_NTFS is not set +# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set +# CONFIG_FEATURE_VOLUMEID_UDF is not set +# CONFIG_FEATURE_VOLUMEID_LUKS is not set +# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set +# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set +# CONFIG_FEATURE_VOLUMEID_ROMFS is not set +# CONFIG_FEATURE_VOLUMEID_SYSV is not set +# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set +# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set + +# +# Miscellaneous Utilities +# +# CONFIG_CONSPY is not set +# CONFIG_NANDWRITE is not set +# CONFIG_NANDDUMP is not set +# CONFIG_SETSERIAL 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_ADJTIMEX is not set +# CONFIG_BBCONFIG is not set +# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set +# CONFIG_BEEP is not set +CONFIG_FEATURE_BEEP_FREQ=0 +CONFIG_FEATURE_BEEP_LENGTH_MS=0 +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_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 +# 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 is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +# CONFIG_FBSPLASH is not set +# 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 is not set +# 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_LESS=y +CONFIG_FEATURE_LESS_MAXLINES=9999999 +CONFIG_FEATURE_LESS_BRACKETS=y +CONFIG_FEATURE_LESS_FLAGS=y +CONFIG_FEATURE_LESS_MARKS=y +CONFIG_FEATURE_LESS_REGEXP=y +CONFIG_FEATURE_LESS_WINCH=y +CONFIG_FEATURE_LESS_DASHCMD=y +CONFIG_FEATURE_LESS_LINENUMS=y +# CONFIG_HDPARM is not set +# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set +# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set +# 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_MAKEDEVS is not set +# CONFIG_FEATURE_MAKEDEVS_LEAF is not set +# CONFIG_FEATURE_MAKEDEVS_TABLE is not set +CONFIG_MAN=y +# CONFIG_MICROCOM is not set +# CONFIG_MOUNTPOINT is not set +CONFIG_MT=y +# CONFIG_RAIDAUTORUN is not set +# CONFIG_READAHEAD is not set +# CONFIG_RFKILL is not set +# CONFIG_RUNLEVEL is not set +# CONFIG_RX is not set +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 + +# +# Networking Utilities +# +# CONFIG_NAMEIF is not set +# CONFIG_FEATURE_NAMEIF_EXTENDED is not set +# CONFIG_NBDCLIENT is not set +CONFIG_NC=y +CONFIG_NC_SERVER=y +CONFIG_NC_EXTRA=y +# CONFIG_NC_110_COMPAT is not set +# CONFIG_PING is not set +# CONFIG_PING6 is not set +# CONFIG_FEATURE_FANCY_PING is not set +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 is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +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_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_USE_SENDFILE is not set +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 is not set +# CONFIG_IFUPDOWN is not set +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 +# 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 is not set +# CONFIG_FEATURE_IP_ADDRESS is not set +# CONFIG_FEATURE_IP_LINK is not set +# CONFIG_FEATURE_IP_ROUTE is not set +# CONFIG_FEATURE_IP_TUNNEL is not set +# CONFIG_FEATURE_IP_RULE is not set +# CONFIG_FEATURE_IP_SHORT_FORMS is not set +# 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=y +CONFIG_FEATURE_IPCALC_FANCY=y +CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y +# CONFIG_NETSTAT is not set +# CONFIG_FEATURE_NETSTAT_WIDE is not set +# CONFIG_FEATURE_NETSTAT_PRG is not set +# CONFIG_NSLOOKUP is not set +# CONFIG_NTPD is not set +# CONFIG_FEATURE_NTPD_SERVER is not set +CONFIG_PSCAN=y +# CONFIG_ROUTE is not set +# CONFIG_SLATTACH is not set +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 is not set +# CONFIG_TRACEROUTE6 is not set +# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set +# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set +# CONFIG_TUNCTL is not set +# CONFIG_FEATURE_TUNCTL_UG is not set +# CONFIG_UDHCPD is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set +CONFIG_DHCPD_LEASES_FILE="" +# CONFIG_UDHCPC is not set +# CONFIG_FEATURE_UDHCPC_ARPING is not set +# CONFIG_FEATURE_UDHCP_PORT is not set +CONFIG_UDHCP_DEBUG=0 +# CONFIG_FEATURE_UDHCP_RFC3397 is not set +# CONFIG_FEATURE_UDHCP_8021Q is not set +CONFIG_UDHCPC_DEFAULT_SCRIPT="" +CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 +CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" +CONFIG_UDPSVD=y +# 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 +# +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_MPSTAT=y +CONFIG_NMETER=y +# CONFIG_PMAP is not set +# CONFIG_POWERTOP is not set +CONFIG_PSTREE=y +CONFIG_PWDX=y +CONFIG_SMEMCAP=y +# CONFIG_FREE is not set +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=y +# CONFIG_FEATURE_PS_TIME is not set +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 is not set +# CONFIG_UPTIME is not set +CONFIG_WATCH=y + +# +# Runit Utilities +# +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_CHPST=y +CONFIG_SETUIDGID=y +CONFIG_ENVUIDGID=y +CONFIG_ENVDIR=y +CONFIG_SOFTLIMIT=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_ECHO=y +CONFIG_ASH_PRINTF=y +CONFIG_ASH_TEST=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=y +CONFIG_HUSH_BASH_COMPAT=y +CONFIG_HUSH_BRACE_EXPANSION=y +CONFIG_HUSH_HELP=y +CONFIG_HUSH_INTERACTIVE=y +CONFIG_HUSH_SAVEHISTORY=y +CONFIG_HUSH_JOB=y +CONFIG_HUSH_TICK=y +CONFIG_HUSH_IF=y +CONFIG_HUSH_LOOPS=y +CONFIG_HUSH_CASE=y +CONFIG_HUSH_FUNCTIONS=y +CONFIG_HUSH_LOCAL=y +CONFIG_HUSH_RANDOM_SUPPORT=y +CONFIG_HUSH_EXPORT_N=y +CONFIG_HUSH_MODE_X=y +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_SYSLOGD=y +CONFIG_FEATURE_ROTATE_LOGFILE=y +CONFIG_FEATURE_REMOTE_LOG=y +CONFIG_FEATURE_SYSLOGD_DUP=y +CONFIG_FEATURE_SYSLOGD_CFG=y +CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256 +CONFIG_FEATURE_IPC_SYSLOG=y +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 +CONFIG_LOGREAD=y +CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y +# CONFIG_KLOGD is not set +# CONFIG_FEATURE_KLOGD_KLOGCTL is not set +CONFIG_LOGGER=y diff --git a/busybox/configs/freebsd_defconfig b/busybox/configs/freebsd_defconfig new file mode 100644 index 00000000000000..47e705963254b8 --- /dev/null +++ b/busybox/configs/freebsd_defconfig @@ -0,0 +1,959 @@ +# +# Automatically generated make config: don't edit +# Busybox version: 1.18.1 +# Tue Dec 21 19:47:40 2010 +# +CONFIG_HAVE_DOT_CONFIG=y + +# +# Busybox Settings +# + +# +# General Configuration +# +# CONFIG_DESKTOP is not set +# CONFIG_EXTRA_COMPAT is not set +CONFIG_INCLUDE_SUSv2=y +CONFIG_USE_PORTABLE_CODE=y +# CONFIG_PLATFORM_LINUX is not set +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=y +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_LONG_OPTS=y +CONFIG_FEATURE_DEVPTS=y +# CONFIG_FEATURE_CLEAN_UP is not set +# CONFIG_FEATURE_WTMP is not set +# CONFIG_FEATURE_UTMP is not set +CONFIG_FEATURE_PIDFILE=y +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 + +# +# 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=y +CONFIG_CROSS_COMPILER_PREFIX="" +CONFIG_EXTRA_CFLAGS="" + +# +# 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 +# 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_PASSWORD_MINLEN=6 +CONFIG_MD5_SMALL=1 +CONFIG_FEATURE_FAST_TOP=y +# CONFIG_FEATURE_ETC_NETWORKS is not set +CONFIG_FEATURE_EDITING=y +CONFIG_FEATURE_EDITING_MAX_LEN=1024 +# CONFIG_FEATURE_EDITING_VI is not set +CONFIG_FEATURE_EDITING_HISTORY=30 +# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set +CONFIG_FEATURE_TAB_COMPLETION=y +# CONFIG_FEATURE_USERNAME_COMPLETION is not set +# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set +# 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_MONOTONIC_SYSCALL is not set +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=y +CONFIG_AR=y +CONFIG_FEATURE_AR_LONG_FILENAMES=y +CONFIG_FEATURE_AR_CREATE=y +CONFIG_BUNZIP2=y +CONFIG_BZIP2=y +CONFIG_CPIO=y +# CONFIG_FEATURE_CPIO_O is not set +# CONFIG_FEATURE_CPIO_P is not set +# CONFIG_DPKG is not set +# CONFIG_DPKG_DEB is not set +CONFIG_GUNZIP=y +CONFIG_GZIP=y +CONFIG_FEATURE_GZIP_LONG_OPTIONS=y +CONFIG_LZOP=y +# CONFIG_LZOP_COMPR_HIGH is not set +CONFIG_RPM2CPIO=y +CONFIG_RPM=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 is not set +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 + +# +# Coreutils +# +CONFIG_BASENAME=y +CONFIG_CAT=y +# CONFIG_DATE is not set +# CONFIG_FEATURE_DATE_ISOFMT is not set +# CONFIG_FEATURE_DATE_NANO is not set +# CONFIG_FEATURE_DATE_COMPAT is not set +CONFIG_TEST=y +CONFIG_FEATURE_TEST_64=y +CONFIG_TR=y +CONFIG_FEATURE_TR_CLASSES=y +CONFIG_FEATURE_TR_EQUIV=y +# CONFIG_BASE64 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_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 +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_HOSTID=y +CONFIG_ID=y +CONFIG_INSTALL=y +CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y +CONFIG_LENGTH=y +CONFIG_LN=y +CONFIG_LOGNAME=y +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 is not set +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 is not set +CONFIG_SEQ=y +CONFIG_SHA1SUM=y +CONFIG_SHA256SUM=y +CONFIG_SHA512SUM=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_STAT is not set +# CONFIG_FEATURE_STAT_FORMAT is not set +# CONFIG_STTY is not set +CONFIG_SUM=y +CONFIG_SYNC=y +# CONFIG_TAC is not set +CONFIG_TAIL=y +CONFIG_FEATURE_FANCY_TAIL=y +CONFIG_TEE=y +CONFIG_FEATURE_TEE_USE_BLOCK_IO=y +CONFIG_TOUCH=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_UNAME=y +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_WHO is not set +CONFIG_WHOAMI=y +CONFIG_YES=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y + +# +# Common options for md5sum, sha1sum, sha256sum, sha512sum +# +CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y + +# +# Console Utilities +# +# CONFIG_CHVT is not set +# CONFIG_FGCONSOLE is not set +CONFIG_CLEAR=y +# CONFIG_DEALLOCVT is not set +# CONFIG_DUMPKMAP is not set +# CONFIG_KBD_MODE is not set +# CONFIG_LOADFONT is not set +# CONFIG_LOADKMAP is not set +# CONFIG_OPENVT is not set +CONFIG_RESET=y +CONFIG_RESIZE=y +CONFIG_FEATURE_RESIZE_PRINT=y +# CONFIG_SETCONSOLE is not set +# 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 is not set +# 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 +# +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 is not set +# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set +# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set +CONFIG_WHICH=y + +# +# Editors +# +CONFIG_PATCH=y +CONFIG_AWK=y +CONFIG_FEATURE_AWK_LIBM=y +CONFIG_CMP=y +CONFIG_DIFF=y +CONFIG_FEATURE_DIFF_LONG_OPTIONS=y +CONFIG_FEATURE_DIFF_DIR=y +CONFIG_ED=y +CONFIG_SED=y +CONFIG_VI=y +CONFIG_FEATURE_VI_MAX_LEN=1024 +CONFIG_FEATURE_VI_8BIT=y +CONFIG_FEATURE_VI_COLON=y +CONFIG_FEATURE_VI_YANKMARK=y +CONFIG_FEATURE_VI_SEARCH=y +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_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_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 + +# +# Init Utilities +# +# CONFIG_BOOTCHARTD is not set +# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set +# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set +# CONFIG_HALT is not set +# CONFIG_FEATURE_CALL_TELINIT is not set +CONFIG_TELINIT_PATH="" +# CONFIG_INIT is not set +# CONFIG_FEATURE_USE_INITTAB is not set +# CONFIG_FEATURE_KILL_REMOVED is not set +CONFIG_FEATURE_KILL_DELAY=0 +# CONFIG_FEATURE_INIT_SCTTY is not set +# CONFIG_FEATURE_INIT_SYSLOG is not set +# CONFIG_FEATURE_INIT_QUIET is not set +# CONFIG_FEATURE_INIT_COREDUMPS is not set +# CONFIG_LINUXRC is not set +CONFIG_INIT_TERMINAL_TYPE="" +# CONFIG_MESG is not set + +# +# 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=y +# CONFIG_USE_BB_SHADOW is not set +# CONFIG_USE_BB_CRYPT is not set +# CONFIG_USE_BB_CRYPT_SHA is not set +# CONFIG_ADDUSER is not set +# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set +# CONFIG_FEATURE_CHECK_NAMES is not set +CONFIG_FIRST_SYSTEM_ID=100 +CONFIG_LAST_SYSTEM_ID=999 +CONFIG_ADDGROUP=y +CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS=y +CONFIG_FEATURE_ADDUSER_TO_GROUP=y +# CONFIG_DELUSER is not set +CONFIG_DELGROUP=y +CONFIG_FEATURE_DEL_USER_FROM_GROUP=y +# CONFIG_GETTY is not set +# CONFIG_LOGIN 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_SU is not set +# CONFIG_FEATURE_SU_SYSLOG is not set +# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set +# CONFIG_SULOGIN is not set +# CONFIG_VLOCK is not set + +# +# Linux Ext2 FS Progs +# +CONFIG_CHATTR=y +# CONFIG_FSCK is not set +# CONFIG_LSATTR is not set +# CONFIG_TUNE2FS is not set +# CONFIG_MODINFO is not set +# CONFIG_MODPROBE_SMALL is not set +# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set +# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set +# 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 +# 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="" +CONFIG_DEFAULT_DEPMOD_FILE="" + +# +# Linux System Utilities +# +# CONFIG_BLOCKDEV is not set +CONFIG_REV=y +# CONFIG_ACPID is not set +# CONFIG_FEATURE_ACPID_COMPAT is not set +# CONFIG_BLKID is not set +# CONFIG_DMESG is not set +# CONFIG_FEATURE_DMESG_PRETTY is not set +# CONFIG_FBSET is not set +# CONFIG_FEATURE_FBSET_FANCY is not set +# CONFIG_FEATURE_FBSET_READMODE is not set +# CONFIG_FDFLUSH is not set +# CONFIG_FDFORMAT is not set +# CONFIG_FDISK is not set +CONFIG_FDISK_SUPPORT_LARGE_DISKS=y +# CONFIG_FEATURE_FDISK_WRITABLE is not set +# 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 is not set +# CONFIG_FINDFS is not set +CONFIG_FLOCK=y +# CONFIG_FREERAMDISK is not set +# CONFIG_FSCK_MINIX is not set +# CONFIG_MKFS_EXT2 is not set +# CONFIG_MKFS_MINIX is not set +# CONFIG_FEATURE_MINIX2 is not set +# CONFIG_MKFS_REISER is not set +# CONFIG_MKFS_VFAT is not set +CONFIG_GETOPT=y +CONFIG_FEATURE_GETOPT_LONG=y +CONFIG_HEXDUMP=y +# CONFIG_FEATURE_HEXDUMP_REVERSE is not set +CONFIG_HD=y +# CONFIG_HWCLOCK is not set +# CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set +# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set +# CONFIG_IPCRM is not set +# CONFIG_IPCS is not set +# CONFIG_LOSETUP is not set +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_MKSWAP is not set +# CONFIG_FEATURE_MKSWAP_UUID is not set +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 is not set +CONFIG_READPROFILE=y +# CONFIG_RTCWAKE is not set +# CONFIG_SCRIPT is not set +CONFIG_SCRIPTREPLAY=y +# CONFIG_SETARCH is not set +# CONFIG_SWAPONOFF is not set +# CONFIG_FEATURE_SWAPON_PRI is not set +# CONFIG_SWITCH_ROOT is not set +# CONFIG_UMOUNT is not set +# CONFIG_FEATURE_UMOUNT_ALL is not set +# CONFIG_FEATURE_MOUNT_LOOP is not set +# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set +# CONFIG_FEATURE_MTAB_SUPPORT is not set +# CONFIG_VOLUMEID is not set +# CONFIG_FEATURE_VOLUMEID_EXT is not set +# CONFIG_FEATURE_VOLUMEID_BTRFS is not set +# CONFIG_FEATURE_VOLUMEID_REISERFS is not set +# CONFIG_FEATURE_VOLUMEID_FAT is not set +# CONFIG_FEATURE_VOLUMEID_HFS is not set +# CONFIG_FEATURE_VOLUMEID_JFS is not set +# CONFIG_FEATURE_VOLUMEID_XFS is not set +# CONFIG_FEATURE_VOLUMEID_NTFS is not set +# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set +# CONFIG_FEATURE_VOLUMEID_UDF is not set +# CONFIG_FEATURE_VOLUMEID_LUKS is not set +# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set +# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set +# CONFIG_FEATURE_VOLUMEID_ROMFS is not set +# CONFIG_FEATURE_VOLUMEID_SYSV is not set +# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set +# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set + +# +# Miscellaneous Utilities +# +# CONFIG_CONSPY is not set +# CONFIG_NANDWRITE is not set +# CONFIG_NANDDUMP is not set +# CONFIG_UBIATTACH is not set +# CONFIG_UBIDETACH is not set +# CONFIG_ADJTIMEX is not set +# CONFIG_BBCONFIG is not set +# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set +# CONFIG_BEEP is not set +CONFIG_FEATURE_BEEP_FREQ=0 +CONFIG_FEATURE_BEEP_LENGTH_MS=0 +# CONFIG_CHAT is not set +# CONFIG_FEATURE_CHAT_NOFAIL is not set +# CONFIG_FEATURE_CHAT_TTY_HIFI is not set +# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set +# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set +# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set +# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set +# CONFIG_FEATURE_CHAT_CLR_ABORT is not set +CONFIG_CHRT=y +# CONFIG_CROND is not set +# CONFIG_FEATURE_CROND_D is not set +# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set +CONFIG_FEATURE_CROND_DIR="/var/spool/cron" +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 is not set +# CONFIG_EJECT is not set +# CONFIG_FEATURE_EJECT_SCSI is not set +# CONFIG_FBSPLASH is not set +# 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 is not set +# 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_LESS=y +CONFIG_FEATURE_LESS_MAXLINES=9999999 +CONFIG_FEATURE_LESS_BRACKETS=y +CONFIG_FEATURE_LESS_FLAGS=y +CONFIG_FEATURE_LESS_MARKS=y +CONFIG_FEATURE_LESS_REGEXP=y +# CONFIG_FEATURE_LESS_WINCH is not set +# CONFIG_FEATURE_LESS_DASHCMD is not set +# CONFIG_FEATURE_LESS_LINENUMS is not set +# CONFIG_HDPARM is not set +# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set +# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set +# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set +# 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_MAKEDEVS is not set +# CONFIG_FEATURE_MAKEDEVS_LEAF is not set +# CONFIG_FEATURE_MAKEDEVS_TABLE is not set +# CONFIG_MAN is not set +CONFIG_MICROCOM=y +# CONFIG_MOUNTPOINT is not set +# CONFIG_MT is not set +# CONFIG_RAIDAUTORUN is not set +# CONFIG_READAHEAD is not set +# CONFIG_RFKILL is not set +# CONFIG_RUNLEVEL is not set +# CONFIG_RX is not set +CONFIG_SETSID=y +CONFIG_STRINGS=y +# CONFIG_TASKSET is not set +# CONFIG_FEATURE_TASKSET_FANCY is not set +# CONFIG_TIME is not set +CONFIG_TIMEOUT=y +CONFIG_TTYSIZE=y +CONFIG_VOLNAME=y +# CONFIG_WALL is not set +# CONFIG_WATCHDOG is not set + +# +# Networking Utilities +# +# CONFIG_NBDCLIENT is not set +CONFIG_NC=y +# CONFIG_NC_SERVER is not set +# CONFIG_NC_EXTRA is not set +# CONFIG_NC_110_COMPAT is not set +# CONFIG_FEATURE_IPV6 is not set +# CONFIG_FEATURE_UNIX_LOCAL is not set +# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set +# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +# CONFIG_ARP is not set +# CONFIG_ARPING is not set +# CONFIG_BRCTL is not set +# CONFIG_FEATURE_BRCTL_FANCY is not set +# CONFIG_FEATURE_BRCTL_SHOW is not set +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_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_USE_SENDFILE is not set +# CONFIG_FEATURE_HTTPD_SETUID is not set +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 is not set +# CONFIG_IFUPDOWN is not set +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 +# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set +# CONFIG_INETD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set +# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set +# CONFIG_FEATURE_INETD_RPC is not set +# CONFIG_IP is not set +# CONFIG_FEATURE_IP_ADDRESS is not set +# CONFIG_FEATURE_IP_LINK is not set +# CONFIG_FEATURE_IP_ROUTE is not set +# CONFIG_FEATURE_IP_TUNNEL is not set +# CONFIG_FEATURE_IP_RULE is not set +# CONFIG_FEATURE_IP_SHORT_FORMS is not set +# 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=y +CONFIG_FEATURE_IPCALC_FANCY=y +CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y +# CONFIG_NAMEIF is not set +# CONFIG_FEATURE_NAMEIF_EXTENDED is not set +# CONFIG_NETSTAT is not set +# CONFIG_FEATURE_NETSTAT_WIDE is not set +# CONFIG_FEATURE_NETSTAT_PRG is not set +# CONFIG_NSLOOKUP is not set +# CONFIG_NTPD is not set +# CONFIG_FEATURE_NTPD_SERVER is not set +# CONFIG_PING is not set +# CONFIG_PING6 is not set +# CONFIG_FEATURE_FANCY_PING is not set +CONFIG_PSCAN=y +# CONFIG_ROUTE is not set +# CONFIG_SLATTACH is not set +# CONFIG_TCPSVD is not set +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 is not set +# CONFIG_TRACEROUTE6 is not set +# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set +# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set +# CONFIG_TUNCTL is not set +# CONFIG_FEATURE_TUNCTL_UG is not set +# CONFIG_UDHCPD is not set +# CONFIG_DHCPRELAY is not set +# CONFIG_DUMPLEASES is not set +# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set +CONFIG_DHCPD_LEASES_FILE="" +# CONFIG_UDHCPC is not set +# CONFIG_FEATURE_UDHCPC_ARPING is not set +# CONFIG_FEATURE_UDHCP_PORT is not set +CONFIG_UDHCP_DEBUG=0 +# CONFIG_FEATURE_UDHCP_RFC3397 is not set +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 +# +# CONFIG_LPD is not set +CONFIG_LPR=y +CONFIG_LPQ=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 + +# +# Process Utilities +# +CONFIG_IOSTAT=y +CONFIG_MPSTAT=y +CONFIG_PMAP=y +CONFIG_POWERTOP=y +CONFIG_SMEMCAP=y +# CONFIG_FREE is not set +# CONFIG_FUSER is not set +CONFIG_KILL=y +CONFIG_KILLALL=y +CONFIG_KILLALL5=y +# CONFIG_NMETER is not set +CONFIG_PGREP=y +# CONFIG_PIDOF is not set +# CONFIG_FEATURE_PIDOF_SINGLE is not set +# CONFIG_FEATURE_PIDOF_OMIT is not set +CONFIG_PKILL=y +CONFIG_PS=y +CONFIG_FEATURE_PS_WIDE=y +# CONFIG_FEATURE_PS_TIME is not set +# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set +# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set +CONFIG_RENICE=y +CONFIG_BB_SYSCTL=y +# CONFIG_TOP is not set +# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set +# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set +# CONFIG_FEATURE_TOP_SMP_CPU is not set +# CONFIG_FEATURE_TOP_DECIMALS is not set +# CONFIG_FEATURE_TOP_SMP_PROCESS is not set +# CONFIG_FEATURE_TOPMEM is not set +CONFIG_FEATURE_SHOW_THREADS=y +# CONFIG_UPTIME is not set +CONFIG_WATCH=y + +# +# Runit Utilities +# +# 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_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 +# 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 is not set +# CONFIG_ASH_JOB_CONTROL is not set +# CONFIG_ASH_ALIAS is not set +# CONFIG_ASH_GETOPTS is not set +# CONFIG_ASH_ECHO is not set +# CONFIG_ASH_PRINTF is not set +# CONFIG_ASH_TEST is not set +# CONFIG_ASH_CMDCMD is not set +# CONFIG_ASH_MAIL is not set +# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set +# CONFIG_ASH_RANDOM_SUPPORT is not set +# CONFIG_ASH_EXPAND_PRMT is not set +# CONFIG_CTTYHACK is not set +# 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_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 is not set +# CONFIG_FEATURE_SH_STANDALONE is not set +# CONFIG_FEATURE_SH_NOFORK is not set + +# +# System Logging Utilities +# +CONFIG_SYSLOGD=y +CONFIG_FEATURE_ROTATE_LOGFILE=y +CONFIG_FEATURE_REMOTE_LOG=y +# CONFIG_FEATURE_SYSLOGD_DUP is not set +CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=256 +CONFIG_FEATURE_IPC_SYSLOG=y +CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16 +CONFIG_LOGREAD=y +CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y +# CONFIG_KLOGD is not set +# CONFIG_FEATURE_KLOGD_KLOGCTL is not set +CONFIG_LOGGER=y diff --git a/busybox/console-tools/Config.src b/busybox/console-tools/Config.src new file mode 100644 index 00000000000000..c30caf0e1ccbd6 --- /dev/null +++ b/busybox/console-tools/Config.src @@ -0,0 +1,10 @@ +# +# For a description of the syntax of this configuration file, +# see docs/Kconfig-language.txt. +# + +menu "Console Utilities" + +INSERT + +endmenu diff --git a/busybox/console-tools/Kbuild.src b/busybox/console-tools/Kbuild.src new file mode 100644 index 00000000000000..6b4fb747007c3f --- /dev/null +++ b/busybox/console-tools/Kbuild.src @@ -0,0 +1,9 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +lib-y:= + +INSERT diff --git a/busybox/console-tools/chvt.c b/busybox/console-tools/chvt.c new file mode 100644 index 00000000000000..75380a90b95b4d --- /dev/null +++ b/busybox/console-tools/chvt.c @@ -0,0 +1,34 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chvt implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config CHVT +//config: bool "chvt (2 kb)" +//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_NOEXEC(chvt, chvt, BB_DIR_USR_BIN, BB_SUID_DROP, chvt)) + +//kbuild:lib-$(CONFIG_CHVT) += chvt.o + +//usage:#define chvt_trivial_usage +//usage: "N" +//usage:#define chvt_full_usage "\n\n" +//usage: "Change the foreground virtual terminal to /dev/ttyN" + +#include "libbb.h" + +int chvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chvt_main(int argc UNUSED_PARAM, char **argv) +{ + int num = xatou_range(single_argv(argv), 1, 63); + console_make_active(get_console_fd_or_die(), num); + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/clear.c b/busybox/console-tools/clear.c new file mode 100644 index 00000000000000..09ef1ea68265bd --- /dev/null +++ b/busybox/console-tools/clear.c @@ -0,0 +1,33 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini clear implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config CLEAR +//config: bool "clear (tiny)" +//config: default y +//config: help +//config: This program clears the terminal screen. + +//applet:IF_CLEAR(APPLET_NOFORK(clear, clear, BB_DIR_USR_BIN, BB_SUID_DROP, clear)) + +//kbuild:lib-$(CONFIG_CLEAR) += clear.o + +//usage:#define clear_trivial_usage +//usage: "" +//usage:#define clear_full_usage "\n\n" +//usage: "Clear screen" + +#include "libbb.h" + +#define ESC "\033" + +int clear_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int clear_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + /* home; clear to the end of screen */ + return full_write1_str(ESC"[H" ESC"[J") != 6; +} diff --git a/busybox/console-tools/deallocvt.c b/busybox/console-tools/deallocvt.c new file mode 100644 index 00000000000000..05731fb787de2f --- /dev/null +++ b/busybox/console-tools/deallocvt.c @@ -0,0 +1,46 @@ +/* vi: set sw=4 ts=4: */ +/* + * Disallocate virtual terminal(s) + * + * Copyright (C) 2003 by Tito Ragusa + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config DEALLOCVT +//config: bool "deallocvt (1.9 kb)" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: This program deallocates unused virtual consoles. + +//applet:IF_DEALLOCVT(APPLET_NOEXEC(deallocvt, deallocvt, BB_DIR_USR_BIN, BB_SUID_DROP, deallocvt)) + +//kbuild:lib-$(CONFIG_DEALLOCVT) += deallocvt.o + +//usage:#define deallocvt_trivial_usage +//usage: "[N]" +//usage:#define deallocvt_full_usage "\n\n" +//usage: "Deallocate unused virtual terminal /dev/ttyN" + +#include "libbb.h" + +/* From */ +enum { VT_DISALLOCATE = 0x5608 }; /* free memory associated to vt */ + +int deallocvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int deallocvt_main(int argc UNUSED_PARAM, char **argv) +{ + /* num = 0 deallocate all unused consoles */ + int num = 0; + + if (argv[1]) { + if (argv[2]) + bb_show_usage(); + num = xatou_range(argv[1], 1, 63); + } + + /* double cast suppresses "cast to ptr from int of different size" */ + xioctl(get_console_fd_or_die(), VT_DISALLOCATE, (void *)(ptrdiff_t)num); + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/dumpkmap.c b/busybox/console-tools/dumpkmap.c new file mode 100644 index 00000000000000..f1c9287b5e9349 --- /dev/null +++ b/busybox/console-tools/dumpkmap.c @@ -0,0 +1,89 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dumpkmap implementation for busybox + * + * Copyright (C) Arne Bernin + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config DUMPKMAP +//config: bool "dumpkmap (1.3 kb)" +//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_NOEXEC(dumpkmap, dumpkmap, BB_DIR_BIN, BB_SUID_DROP, dumpkmap)) +/* bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed. */ + +//kbuild:lib-$(CONFIG_DUMPKMAP) += dumpkmap.o + +//usage:#define dumpkmap_trivial_usage +//usage: "> keymap" +//usage:#define dumpkmap_full_usage "\n\n" +//usage: "Print a binary keyboard translation table to stdout" +//usage: +//usage:#define dumpkmap_example_usage +//usage: "$ dumpkmap > keymap\n" + +#include "libbb.h" +#include "common_bufsiz.h" + +/* From */ +struct kbentry { + unsigned char kb_table; + unsigned char kb_index; + unsigned short kb_value; +}; +#define KDGKBENT 0x4B46 /* gets one entry in translation table */ + +/* From */ +#define NR_KEYS 128 +#define MAX_NR_KEYMAPS 256 + +int dumpkmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dumpkmap_main(int argc UNUSED_PARAM, char **argv) +{ + struct kbentry ke; + int i, j, fd; + + /* When user accidentally runs "dumpkmap FILE" + * instead of "dumpkmap >FILE", we'd dump binary stuff to tty. + * Let's prevent it: + */ + if (argv[1]) + bb_show_usage(); +/* bb_warn_ignoring_args(argv[1]);*/ + + fd = get_console_fd_or_die(); + +#define flags bb_common_bufsiz1 + setup_common_bufsiz(); + /* 0 1 2 3 4 5 6 7 8 9 a b c=12 */ + memcpy(flags, "bkeymap\1\1\1\0\1\1\1\0\1\1\1\0\1", + /* Can use sizeof, or sizeof-1. sizeof is even, using that */ + /****/ sizeof("bkeymap\1\1\1\0\1\1\1\0\1\1\1\0\1") + ); + write(STDOUT_FILENO, flags, 7 + MAX_NR_KEYMAPS); +#define flags7 (flags + 7) + + for (i = 0; i < 13; i++) { + if (flags7[i]) { + for (j = 0; j < NR_KEYS; j++) { + ke.kb_index = j; + ke.kb_table = i; + if (!ioctl_or_perror(fd, KDGKBENT, &ke, + "ioctl(KDGKBENT{%d,%d}) failed", + j, i) + ) { + write(STDOUT_FILENO, &ke.kb_value, 2); + } + } + } + } + if (ENABLE_FEATURE_CLEAN_UP) { + close(fd); + } + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/fgconsole.c b/busybox/console-tools/fgconsole.c new file mode 100644 index 00000000000000..a353becd5fb4b7 --- /dev/null +++ b/busybox/console-tools/fgconsole.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini fgconsole implementation for busybox + * + * Copyright (C) 2010 by Grigory Batalov + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config FGCONSOLE +//config: bool "fgconsole (1.6 kb)" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: This program prints active (foreground) console number. + +//applet:IF_FGCONSOLE(APPLET_NOEXEC(fgconsole, fgconsole, BB_DIR_USR_BIN, BB_SUID_DROP, fgconsole)) + +//kbuild:lib-$(CONFIG_FGCONSOLE) += fgconsole.o + +//usage:#define fgconsole_trivial_usage +//usage: "" +//usage:#define fgconsole_full_usage "\n\n" +//usage: "Get active console" + +#include "libbb.h" + +/* From */ +struct vt_stat { + unsigned short v_active; /* active vt */ + unsigned short v_signal; /* signal to send */ + unsigned short v_state; /* vt bitmask */ +}; +enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */ + +int fgconsole_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fgconsole_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + struct vt_stat vtstat; + + vtstat.v_active = 0; + xioctl(get_console_fd_or_die(), VT_GETSTATE, &vtstat); + printf("%d\n", vtstat.v_active); + + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/kbd_mode.c b/busybox/console-tools/kbd_mode.c new file mode 100644 index 00000000000000..f16449dcdd3385 --- /dev/null +++ b/busybox/console-tools/kbd_mode.c @@ -0,0 +1,95 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini kbd_mode implementation for busybox + * + * Copyright (C) 2007 Loic Grenie + * written using Andries Brouwer 's kbd_mode from + * console-utils v0.2.3, licensed under GNU GPLv2 + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config KBD_MODE +//config: bool "kbd_mode (4 kb)" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: This program reports and sets keyboard mode. + +//applet:IF_KBD_MODE(APPLET_NOEXEC(kbd_mode, kbd_mode, BB_DIR_BIN, BB_SUID_DROP, kbd_mode)) + +//kbuild:lib-$(CONFIG_KBD_MODE) += kbd_mode.o + +//usage:#define kbd_mode_trivial_usage +//usage: "[-a|k|s|u] [-C TTY]" +//usage:#define kbd_mode_full_usage "\n\n" +//usage: "Report or set VT console keyboard mode\n" +//usage: "\n -a Default (ASCII)" +//usage: "\n -k Medium-raw (keycode)" +//usage: "\n -s Raw (scancode)" +//usage: "\n -u Unicode (utf-8)" +//usage: "\n -C TTY Affect TTY" + +#include "libbb.h" +#include + +int kbd_mode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int kbd_mode_main(int argc UNUSED_PARAM, char **argv) +{ + enum { + SCANCODE = (1 << 0), + ASCII = (1 << 1), + MEDIUMRAW = (1 << 2), + UNICODE = (1 << 3), + }; + int fd; + unsigned opt; + const char *tty_name; + + opt = getopt32(argv, "sakuC:", &tty_name); + if (opt & 0x10) { + opt &= 0xf; /* clear -C bit, see (*) */ + fd = xopen_nonblocking(tty_name); + } else { + /* kbd-2.0.3 tries in sequence: + * fd#0, /dev/tty, /dev/tty0. + * get_console_fd_or_die: /dev/console, /dev/tty0, /dev/tty. + * kbd-2.0.3 checks KDGKBTYPE, get_console_fd_or_die checks too. + */ + fd = get_console_fd_or_die(); + } + + if (!opt) { /* print current setting */ + const char *mode = "unknown"; + int m; + + xioctl(fd, KDGKBMODE, &m); + if (m == K_RAW) + mode = "raw (scancode)"; + else if (m == K_XLATE) + mode = "default (ASCII)"; + else if (m == K_MEDIUMRAW) + mode = "mediumraw (keycode)"; + else if (m == K_UNICODE) + mode = "Unicode (UTF-8)"; + else if (m == 4 /*K_OFF*/) /* kbd-2.0.3 does not show this mode, says "unknown" */ + mode = "off"; + printf("The keyboard is in %s mode\n", mode); + } else { + /* here we depend on specific bits assigned to options (*) + * KDSKBMODE constants have these values: + * #define K_RAW 0x00 + * #define K_XLATE 0x01 + * #define K_MEDIUMRAW 0x02 + * #define K_UNICODE 0x03 + * #define K_OFF 0x04 + * (looks like "-ak" together would cause the same effect as -u) + */ + opt = opt & UNICODE ? 3 : opt >> 1; + /* double cast prevents warnings about widening conversion */ + xioctl(fd, KDSKBMODE, (void*)(ptrdiff_t)opt); + } + + if (ENABLE_FEATURE_CLEAN_UP) + close(fd); + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/loadfont.c b/busybox/console-tools/loadfont.c new file mode 100644 index 00000000000000..81d0c3db46c2db --- /dev/null +++ b/busybox/console-tools/loadfont.c @@ -0,0 +1,535 @@ +/* vi: set sw=4 ts=4: */ +/* + * loadfont.c - Eugene Crosser & Andries Brouwer + * + * Version 0.96bb + * + * Loads the console font, and possibly the corresponding screen map(s). + * (Adapted for busybox by Matej Vela.) + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config LOADFONT +//config: bool "loadfont (5.4 kb)" +//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 (26 kb)" +//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 PSF2 console fonts" +//config: default y +//config: depends on LOADFONT || SETFONT +//config: +//config:config FEATURE_LOADFONT_RAW +//config: bool "Support old (raw) console fonts" +//config: default y +//config: depends on LOADFONT || SETFONT + +//applet:IF_LOADFONT(APPLET_NOEXEC(loadfont, loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP, loadfont)) +//applet:IF_SETFONT(APPLET_NOEXEC(setfont, setfont, BB_DIR_USR_SBIN, BB_SUID_DROP, setfont)) + +//kbuild:lib-$(CONFIG_LOADFONT) += loadfont.o +//kbuild:lib-$(CONFIG_SETFONT) += loadfont.o + +#include "libbb.h" +#include + +#ifndef KDFONTOP +# define KDFONTOP 0x4B72 +struct console_font_op { + unsigned op; /* KD_FONT_OP_* */ + unsigned flags; /* KD_FONT_FLAG_* */ + unsigned width, height; + unsigned charcount; + unsigned char *data; /* font data with height fixed to 32 */ +}; +# define KD_FONT_OP_SET 0 /* Set font */ +# define KD_FONT_OP_GET 1 /* Get font */ +# define KD_FONT_OP_SET_DEFAULT 2 /* Set font to default, data points to name / NULL */ +# define KD_FONT_OP_COPY 3 /* Copy from another console */ +# define KD_FONT_FLAG_OLD 0x80000000 /* Invoked via old interface */ +# define KD_FONT_FLAG_DONT_RECALC 1 /* Don't call adjust_height() */ + /* (Used internally for PIO_FONT support) */ +#endif /* KDFONTOP */ + + +enum { + PSF1_MAGIC0 = 0x36, + PSF1_MAGIC1 = 0x04, + PSF1_MODE512 = 0x01, + PSF1_MODEHASTAB = 0x02, + PSF1_MODEHASSEQ = 0x04, + PSF1_MAXMODE = 0x05, + PSF1_STARTSEQ = 0xfffe, + PSF1_SEPARATOR = 0xffff, +}; + +struct psf1_header { + unsigned char magic[2]; /* Magic number */ + unsigned char mode; /* PSF font mode */ + unsigned char charsize; /* Character size */ +}; + +#define psf1h(x) ((struct psf1_header*)(x)) + +#define PSF1_MAGIC_OK(x) ( \ + (x)->magic[0] == PSF1_MAGIC0 \ + && (x)->magic[1] == PSF1_MAGIC1 \ +) + +#if ENABLE_FEATURE_LOADFONT_PSF2 +enum { + PSF2_MAGIC0 = 0x72, + PSF2_MAGIC1 = 0xb5, + PSF2_MAGIC2 = 0x4a, + PSF2_MAGIC3 = 0x86, + PSF2_HAS_UNICODE_TABLE = 0x01, + PSF2_MAXVERSION = 0, + PSF2_STARTSEQ = 0xfe, + PSF2_SEPARATOR = 0xff +}; + +struct psf2_header { + unsigned char magic[4]; + unsigned int version; + unsigned int headersize; /* offset of bitmaps in file */ + unsigned int flags; + unsigned int length; /* number of glyphs */ + unsigned int charsize; /* number of bytes for each character */ + unsigned int height; /* max dimensions of glyphs */ + unsigned int width; /* charsize = height * ((width + 7) / 8) */ +}; + +#define psf2h(x) ((struct psf2_header*)(x)) + +#define PSF2_MAGIC_OK(x) ( \ + (x)->magic[0] == PSF2_MAGIC0 \ + && (x)->magic[1] == PSF2_MAGIC1 \ + && (x)->magic[2] == PSF2_MAGIC2 \ + && (x)->magic[3] == PSF2_MAGIC3 \ +) +#endif /* ENABLE_FEATURE_LOADFONT_PSF2 */ + + +static void do_loadfont(int fd, unsigned char *inbuf, int height, int width, int charsize, int fontsize) +{ + unsigned char *buf; + int charwidth = 32 * ((width+7)/8); + int i; + + if (height < 1 || height > 32 || width < 1 || width > 32) + bb_error_msg_and_die("bad character size %dx%d", height, width); + + buf = xzalloc(charwidth * ((fontsize < 128) ? 128 : fontsize)); + for (i = 0; i < fontsize; i++) + memcpy(buf + (i*charwidth), inbuf + (i*charsize), charsize); + + { /* KDFONTOP */ + struct console_font_op cfo; + cfo.op = KD_FONT_OP_SET; + cfo.flags = 0; + cfo.width = width; + cfo.height = height; + cfo.charcount = fontsize; + cfo.data = buf; + xioctl(fd, KDFONTOP, &cfo); + } + + free(buf); +} + +/* + * Format of the Unicode information: + * + * For each font position ** + * where is a 2-byte little endian Unicode value (PSF1) + * or an UTF-8 coded value (PSF2), + * = *, = psf1 ? 0xFFFE : 0xFE, + * = psf1 ? 0xFFFF : 0xFF. + * and * denotes zero or more occurrences of the preceding item. + * + * Semantics: + * The leading * part gives Unicode symbols that are all + * represented by this font position. The following sequences + * are sequences of Unicode symbols - probably a symbol + * together with combining accents - also represented by + * this font position. + * + * Example: + * At the font position for a capital A-ring glyph, we + * may have: + * 00C5,212B,FFFE,0041,030A,FFFF + * Some font positions may be described by sequences only, + * namely when there is no precomposed Unicode value for the glyph. + */ +#if !ENABLE_FEATURE_LOADFONT_PSF2 +#define do_loadtable(fd, inbuf, tailsz, fontsize, psf2) \ + do_loadtable(fd, inbuf, tailsz, fontsize) +#endif +static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize, int psf2) +{ +#if !ENABLE_FEATURE_LOADFONT_PSF2 +/* gcc 4.3.1 code size: */ +# define psf2 0 /* +0 bytes */ +// const int psf2 = 0; /* +8 bytes */ +// enum { psf2 = 0 }; /* +13 bytes */ +#endif + struct unimapinit advice; + struct unimapdesc ud; + struct unipair *up; + int ct = 0, maxct; + int glyph; + uint16_t unicode; + + maxct = tailsz; /* more than enough */ + up = xmalloc(maxct * sizeof(*up)); + + for (glyph = 0; glyph < fontsize; glyph++) { + while (tailsz > 0) { + if (!psf2) { /* PSF1 */ + unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0]; + tailsz -= 2; + inbuf += 2; + if (unicode == PSF1_SEPARATOR) + break; + } else { /* PSF2 */ +#if ENABLE_FEATURE_LOADFONT_PSF2 + --tailsz; + unicode = *inbuf++; + if (unicode == PSF2_SEPARATOR) { + break; + } else if (unicode == PSF2_STARTSEQ) { + bb_error_msg_and_die("unicode sequences not implemented"); + } else if (unicode >= 0xC0) { + if (unicode >= 0xFC) + unicode &= 0x01, maxct = 5; + else if (unicode >= 0xF8) + unicode &= 0x03, maxct = 4; + else if (unicode >= 0xF0) + unicode &= 0x07, maxct = 3; + else if (unicode >= 0xE0) + unicode &= 0x0F, maxct = 2; + else + unicode &= 0x1F, maxct = 1; + do { + if (tailsz <= 0 || *inbuf < 0x80 || *inbuf > 0xBF) + bb_error_msg_and_die("illegal UTF-8 character"); + --tailsz; + unicode = (unicode << 6) + (*inbuf++ & 0x3F); + } while (--maxct > 0); + } else if (unicode >= 0x80) { + bb_error_msg_and_die("illegal UTF-8 character"); + } +#else + return; +#endif + } + up[ct].unicode = unicode; + up[ct].fontpos = glyph; + ct++; + } + } + + /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP + * this printf did not work on many kernels */ + + advice.advised_hashsize = 0; + advice.advised_hashstep = 0; + advice.advised_hashlevel = 0; + xioctl(fd, PIO_UNIMAPCLR, &advice); + ud.entry_ct = ct; + ud.entries = up; + xioctl(fd, PIO_UNIMAP, &ud); +#undef psf2 +} + +static void do_load(int fd, unsigned char *buffer, size_t len) +{ + int height; + int width = 8; + int charsize; + int fontsize = 256; + int has_table = 0; + unsigned char *font = buffer; + unsigned char *table; + + if (len >= sizeof(struct psf1_header) && PSF1_MAGIC_OK(psf1h(buffer))) { + if (psf1h(buffer)->mode > PSF1_MAXMODE) + bb_error_msg_and_die("unsupported psf file mode"); + if (psf1h(buffer)->mode & PSF1_MODE512) + fontsize = 512; + if (psf1h(buffer)->mode & PSF1_MODEHASTAB) + has_table = 1; + height = charsize = psf1h(buffer)->charsize; + font += sizeof(struct psf1_header); + } else +#if ENABLE_FEATURE_LOADFONT_PSF2 + if (len >= sizeof(struct psf2_header) && PSF2_MAGIC_OK(psf2h(buffer))) { + if (psf2h(buffer)->version > PSF2_MAXVERSION) + bb_error_msg_and_die("unsupported psf file version"); + fontsize = psf2h(buffer)->length; + if (psf2h(buffer)->flags & PSF2_HAS_UNICODE_TABLE) + has_table = 2; + charsize = psf2h(buffer)->charsize; + height = psf2h(buffer)->height; + width = psf2h(buffer)->width; + font += psf2h(buffer)->headersize; + } else +#endif +#if ENABLE_FEATURE_LOADFONT_RAW + if (len == 9780) { /* file with three code pages? */ + charsize = height = 16; + font += 40; + } else if ((len & 0377) == 0) { /* bare font */ + charsize = height = len / 256; + } else +#endif + { + bb_error_msg_and_die("input file: bad length or unsupported font type"); + } + +#if !defined(PIO_FONTX) || defined(__sparc__) + if (fontsize != 256) + bb_error_msg_and_die("only fontsize 256 supported"); +#endif + + table = font + fontsize * charsize; + buffer += len; + + if (table > buffer || (!has_table && table != buffer)) + bb_error_msg_and_die("input file: bad length"); + + do_loadfont(fd, font, height, width, charsize, fontsize); + + if (has_table) + do_loadtable(fd, table, buffer - table, fontsize, has_table - 1); +} + + +#if ENABLE_LOADFONT +//usage:#define loadfont_trivial_usage +//usage: "< font" +//usage:#define loadfont_full_usage "\n\n" +//usage: "Load a console font from stdin" +/* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */ +//usage: +//usage:#define loadfont_example_usage +//usage: "$ loadfont < /etc/i18n/fontname\n" +int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int loadfont_main(int argc UNUSED_PARAM, char **argv) +{ + size_t len; + unsigned char *buffer; + + // no arguments allowed! + getopt32(argv, "^" "" "\0" "=0"); + + /* + * 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 = 128*1024; + buffer = xmalloc_read(STDIN_FILENO, &len); + // xmalloc_open_zipped_read_close(filename, &len); + if (!buffer) + bb_perror_msg_and_die("error reading input font"); + do_load(get_console_fd_or_die(), buffer, len); + + return EXIT_SUCCESS; +} +#endif + + +#if ENABLE_SETFONT +/* kbd-1.12: +setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig] +[-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console] +[-hNN] [-v] [-V] + +-h NN Override font height +-o file + Save previous font in file +-O file + Save previous font and Unicode map in file +-om file + Store console map in file +-ou file + Save previous Unicode map in file +-m file + Load console map or Unicode console map from file +-u file + Load Unicode table describing the font from file + Example: + # cp866 + 0x00-0x7f idem + # + 0x80 U+0410 # CYRILLIC CAPITAL LETTER A + 0x81 U+0411 # CYRILLIC CAPITAL LETTER BE + 0x82 U+0412 # CYRILLIC CAPITAL LETTER VE +-C console + Set the font for the indicated console +-v Verbose +-V Version +*/ +//usage:#define setfont_trivial_usage +//usage: "FONT [-m MAPFILE] [-C TTY]" +//usage:#define setfont_full_usage "\n\n" +//usage: "Load a console font\n" +//usage: "\n -m MAPFILE Load console screen map" +//usage: "\n -C TTY Affect TTY instead of /dev/tty" +//usage: +//usage:#define setfont_example_usage +//usage: "$ setfont -m koi8-r /etc/i18n/fontname\n" + +# if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP +static int ctoi(char *s) +{ + if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0') + return s[1]; + // U+ means 0x + if (s[0] == 'U' && s[1] == '+') { + s[0] = '0'; + s[1] = 'x'; + } + if (!isdigit(s[0])) + return -1; + return xstrtoul(s, 0); +} +# endif + +int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int setfont_main(int argc UNUSED_PARAM, char **argv) +{ + size_t len; + unsigned opts; + int fd; + unsigned char *buffer; + char *mapfilename; + const char *tty_name = CURRENT_TTY; + + opts = getopt32(argv, "^" "m:C:" "\0" "=1", &mapfilename, &tty_name); + argv += optind; + + fd = xopen_nonblocking(tty_name); + + if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not "" + if (*argv[0] != '/') { + // goto default fonts location. don't die if doesn't exist + chdir(CONFIG_DEFAULT_SETFONT_DIR "/consolefonts"); + } + } + // load font + len = 128*1024; + buffer = xmalloc_open_zipped_read_close(*argv, &len); + if (!buffer) + bb_simple_perror_msg_and_die(*argv); + do_load(fd, buffer, len); + + // load the screen map, if any + if (opts & 1) { // -m + unsigned mode = PIO_SCRNMAP; + void *map; + + if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not "" + if (mapfilename[0] != '/') { + // goto default keymaps location + chdir(CONFIG_DEFAULT_SETFONT_DIR "/consoletrans"); + } + } + // fetch keymap + map = xmalloc_open_zipped_read_close(mapfilename, &len); + if (!map) + bb_simple_perror_msg_and_die(mapfilename); + // file size is 256 or 512 bytes? -> assume binary map + if (len == E_TABSZ || len == 2*E_TABSZ) { + if (len == 2*E_TABSZ) + mode = PIO_UNISCRNMAP; + } +# if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP + // assume textual Unicode console maps: + // 0x00 U+0000 # NULL (NUL) + // 0x01 U+0001 # START OF HEADING (SOH) + // 0x02 U+0002 # START OF TEXT (STX) + // 0x03 U+0003 # END OF TEXT (ETX) + else { + int i; + char *token[2]; + parser_t *parser; + + if (ENABLE_FEATURE_CLEAN_UP) + free(map); + map = xmalloc(E_TABSZ * sizeof(unsigned short)); + +#define unicodes ((unsigned short *)map) + // fill vanilla map + for (i = 0; i < E_TABSZ; i++) + unicodes[i] = 0xf000 + i; + + parser = config_open(mapfilename); + while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) { + // parse code/value pair + int a = ctoi(token[0]); + int b = ctoi(token[1]); + if (a < 0 || a >= E_TABSZ + || b < 0 || b > 65535 + ) { + bb_error_msg_and_die("map format"); + } + // patch map + unicodes[a] = b; + // unicode character is met? + if (b > 255) + mode = PIO_UNISCRNMAP; + } + if (ENABLE_FEATURE_CLEAN_UP) + config_close(parser); + + if (mode != PIO_UNISCRNMAP) { +#define asciis ((unsigned char *)map) + for (i = 0; i < E_TABSZ; i++) + asciis[i] = unicodes[i]; +#undef asciis + } +#undef unicodes + } +# endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP + + // do set screen map + xioctl(fd, mode, map); + + if (ENABLE_FEATURE_CLEAN_UP) + free(map); + } + + return EXIT_SUCCESS; +} +#endif diff --git a/busybox/console-tools/loadkmap.c b/busybox/console-tools/loadkmap.c new file mode 100644 index 00000000000000..404aba1fb1bd4c --- /dev/null +++ b/busybox/console-tools/loadkmap.c @@ -0,0 +1,103 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini loadkmap implementation for busybox + * + * Copyright (C) 1998 Enrique Zanardi + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config LOADKMAP +//config: bool "loadkmap (1.5 kb)" +//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_NOEXEC(loadkmap, loadkmap, BB_DIR_SBIN, BB_SUID_DROP, loadkmap)) + +//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" +////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" + +#include "libbb.h" + +#define BINARY_KEYMAP_MAGIC "bkeymap" + +/* From */ +struct kbentry { + unsigned char kb_table; + unsigned char kb_index; + unsigned short kb_value; +}; +/* sets one entry in translation table */ +#define KDSKBENT 0x4B47 + +/* From */ +#define NR_KEYS 128 +#define MAX_NR_KEYMAPS 256 + +int loadkmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int loadkmap_main(int argc UNUSED_PARAM, char **argv) +{ + struct kbentry ke; + int i, j, fd; + uint16_t ibuff[NR_KEYS]; +/* const char *tty_name = CURRENT_TTY; */ + RESERVE_CONFIG_BUFFER(flags, MAX_NR_KEYMAPS); + + /* When user accidentally runs "loadkmap FILE" + * instead of "loadkmap + * hacked by Tito + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config OPENVT +//config: bool "openvt (7 kb)" +//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]" +//usage:#define openvt_full_usage "\n\n" +//usage: "Start PROG on a new virtual terminal\n" +//usage: "\n -c N Use specified VT" +//usage: "\n -s Switch to the VT" +/* //usage: "\n -l Run PROG as login shell (by prepending '-')" */ +//usage: "\n -w Wait for PROG to exit" +//usage: +//usage:#define openvt_example_usage +//usage: "openvt 2 /bin/ash\n" + +#include +#include "libbb.h" + +/* "Standard" openvt's man page (we do not support all of this): + +openvt [-c NUM] [-fsulv] [--] [command [args]] + +Find the first available VT, and run command on it. Stdio is directed +to that VT. If no command is specified then $SHELL is used. + +-c NUM + Use the given VT number, not the first free one. +-f + Force opening a VT: don't try to check if VT is already in use. +-s + Switch to the new VT when starting the command. + The VT of the new command will be made the new current VT. +-u + Figure out the owner of the current VT, and run login as that user. + Suitable to be called by init. Shouldn't be used with -c or -l. +-l + Make the command a login shell: a "-" is prepended to the argv[0] + when command is executed. +-v + Verbose. +-w + Wait for command to complete. If -w and -s are used together, + switch back to the controlling terminal when the command completes. + +bbox: +-u: not implemented +-f: always in effect +-l: not implemented, ignored +-v: ignored +-ws: does NOT switch back +*/ + +/* Helper: does this fd understand VT_xxx? */ +static int not_vt_fd(int fd) +{ + struct vt_stat vtstat; + return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */ +} + +/* Helper: get a fd suitable for VT_xxx */ +static int get_vt_fd(void) +{ + int fd; + + /* Do we, by chance, already have it? */ + for (fd = 0; fd < 3; fd++) + if (!not_vt_fd(fd)) + return fd; + fd = open(DEV_CONSOLE, O_RDONLY | O_NONBLOCK); + if (fd >= 0 && !not_vt_fd(fd)) + return fd; + bb_error_msg_and_die("can't find open VT"); +} + +static int find_free_vtno(void) +{ + int vtno; + int fd = get_vt_fd(); + + errno = 0; + /*xfunc_error_retval = 3; - do we need compat? */ + if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0) + bb_perror_msg_and_die("can't find open VT"); +// Not really needed, grep for DAEMON_CLOSE_EXTRA_FDS +// if (fd > 2) +// close(fd); + return vtno; +} + +/* vfork scares gcc, it generates bigger code. + * Keep it away from main program. + * TODO: move to libbb; or adapt existing libbb's spawn(). + */ +static NOINLINE void vfork_child(char **argv) +{ + if (vfork() == 0) { + /* CHILD */ + /* Try to make this VT our controlling tty */ + setsid(); /* lose old ctty */ + ioctl(STDIN_FILENO, TIOCSCTTY, 0 /* 0: don't forcibly steal */); + //bb_error_msg("our sid %d", getsid(0)); + //bb_error_msg("our pgrp %d", getpgrp()); + //bb_error_msg("VT's sid %d", tcgetsid(0)); + //bb_error_msg("VT's pgrp %d", tcgetpgrp(0)); + BB_EXECVP_or_die(argv); + } +} + +int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int openvt_main(int argc UNUSED_PARAM, char **argv) +{ + char vtname[sizeof(VC_FORMAT) + sizeof(int)*3]; + struct vt_stat vtstat; + char *str_c; + int vtno; + int flags; + enum { + OPT_c = (1 << 0), + OPT_w = (1 << 1), + OPT_s = (1 << 2), + OPT_l = (1 << 3), + OPT_f = (1 << 4), + OPT_v = (1 << 5), + }; + + /* "+" - stop on first non-option */ + flags = getopt32(argv, "+c:wslfv", &str_c); + argv += optind; + + if (flags & OPT_c) { + /* Check for illegal vt number: < 1 or > 63 */ + vtno = xatou_range(str_c, 1, 63); + } else { + vtno = find_free_vtno(); + } + + /* Grab new VT */ + sprintf(vtname, VC_FORMAT, vtno); + /* (Try to) clean up stray open fds above fd 2 */ + bb_daemon_helper(DAEMON_CLOSE_EXTRA_FDS); + close(STDIN_FILENO); + /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */ + xopen(vtname, O_RDWR); + xioctl(STDIN_FILENO, VT_GETSTATE, &vtstat); + + if (flags & OPT_s) { + console_make_active(STDIN_FILENO, vtno); + } + + if (!argv[0]) { + argv--; + argv[0] = (char *) get_shell_name(); + /*argv[1] = NULL; - already is */ + } + + xdup2(STDIN_FILENO, STDOUT_FILENO); + xdup2(STDIN_FILENO, STDERR_FILENO); + +#ifdef BLOAT + { + /* Handle -l (login shell) option */ + const char *prog = argv[0]; + if (flags & OPT_l) + argv[0] = xasprintf("-%s", argv[0]); + } +#endif + + vfork_child(argv); + if (flags & OPT_w) { + /* We have only one child, wait for it */ + safe_waitpid(-1, NULL, 0); /* loops on EINTR */ + if (flags & OPT_s) { + console_make_active(STDIN_FILENO, vtstat.v_active); + // Compat: even with -c N (try to) disallocate: + // # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5 + // openvt: could not deallocate console 9 + xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno); + } + } + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/reset.c b/busybox/console-tools/reset.c new file mode 100644 index 00000000000000..614806ba754e8a --- /dev/null +++ b/busybox/console-tools/reset.c @@ -0,0 +1,65 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini reset implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * Written by Erik Andersen and Kent Robotti + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config RESET +//config: bool "reset (275 bytes)" +//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_NOEXEC(reset, reset, BB_DIR_USR_BIN, BB_SUID_DROP, reset)) + +//kbuild:lib-$(CONFIG_RESET) += reset.o + +//usage:#define reset_trivial_usage +//usage: "" +//usage:#define reset_full_usage "\n\n" +//usage: "Reset the screen" + +/* "Standard" version of this tool is in ncurses package */ + +#include "libbb.h" + +#define ESC "\033" + +#if ENABLE_STTY +int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +#endif + +int reset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int reset_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + static const char *const args[] = { + "stty", "sane", NULL + }; + + /* no options, no getopt */ + + if (/*isatty(STDIN_FILENO) &&*/ isatty(STDOUT_FILENO)) { + /* See 'man 4 console_codes' for details: + * "ESC c" -- Reset + * "ESC ( B" -- Select G0 Character Set (B = US) + * "ESC [ m" -- Reset all display attributes + * "ESC [ J" -- Erase to the end of screen + * "ESC [ ? 25 h" -- Make cursor visible + */ + printf(ESC"c" ESC"(B" ESC"[m" ESC"[J" ESC"[?25h"); + /* http://bugs.busybox.net/view.php?id=1414: + * people want it to reset echo etc: */ +#if ENABLE_STTY + return stty_main(2, (char**)args); +#else + /* Make sure stdout gets drained before we execvp */ + fflush_all(); + execvp("stty", (char**)args); +#endif + } + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/resize.c b/busybox/console-tools/resize.c new file mode 100644 index 00000000000000..8aa487c41f73b0 --- /dev/null +++ b/busybox/console-tools/resize.c @@ -0,0 +1,103 @@ +/* vi: set sw=4 ts=4: */ +/* + * resize - set terminal width and height. + * + * Copyright 2006 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config RESIZE +//config: bool "resize (756 bytes)" +//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_NOEXEC(resize, resize, BB_DIR_USR_BIN, BB_SUID_DROP, resize)) +/* bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed. */ + +//kbuild:lib-$(CONFIG_RESIZE) += resize.o + +//usage:#define resize_trivial_usage +//usage: "" +//usage:#define resize_full_usage "\n\n" +//usage: "Resize the screen" + +#include "libbb.h" +#include "common_bufsiz.h" + +#define ESC "\033" + +#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) +{ + tcsetattr(STDERR_FILENO, TCSANOW, old_termios_p); + _exit(EXIT_FAILURE); +} + +int resize_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int resize_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + struct termios new; + 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" + * and operate on it - should we do the same? + */ + + tcgetattr(STDERR_FILENO, old_termios_p); /* fiddle echo */ +//TODO: die if the above fails? + memcpy(&new, old_termios_p, sizeof(new)); + new.c_cflag |= (CLOCAL | CREAD); + new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + bb_signals(0 + + (1 << SIGINT) + + (1 << SIGQUIT) + + (1 << SIGTERM) + + (1 << SIGALRM) + , onintr); + tcsetattr(STDERR_FILENO, TCSANOW, &new); + + /* save_cursor_pos 7 + * scroll_whole_screen [r + * put_cursor_waaaay_off [$x;$yH + * get_cursor_pos [6n + * restore_cursor_pos 8 + */ + fprintf(stderr, ESC"7" ESC"[r" ESC"[999;999H" ESC"[6n"); + alarm(3); /* Just in case terminal won't answer */ +//BUG: death by signal won't restore termios + scanf(ESC"[%hu;%huR", &w.ws_row, &w.ws_col); + fprintf(stderr, ESC"8"); + + /* BTW, other versions of resize recalculate w.ws_xpixel, ws.ws_ypixel + * by calculating character cell HxW from old values + * (gotten via TIOCGWINSZ) and recomputing *pixel values */ + ret = ioctl(STDERR_FILENO, TIOCSWINSZ, &w); + + tcsetattr(STDERR_FILENO, TCSANOW, old_termios_p); + + if (ENABLE_FEATURE_RESIZE_PRINT) + printf("COLUMNS=%d;LINES=%d;export COLUMNS LINES;\n", + w.ws_col, w.ws_row); + + return ret; +} diff --git a/busybox/console-tools/setconsole.c b/busybox/console-tools/setconsole.c new file mode 100644 index 00000000000000..7f0f9c711cc0a1 --- /dev/null +++ b/busybox/console-tools/setconsole.c @@ -0,0 +1,65 @@ +/* vi: set sw=4 ts=4: */ +/* + * setconsole.c - redirect system console output + * + * Copyright (C) 2004,2005 Enrik Berkhan + * Copyright (C) 2008 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config SETCONSOLE +//config: bool "setconsole (3.7 kb)" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Redirect writes to /dev/console to another device, +//config: like the current tty while logged in via telnet. +//config: This does not redirect kernel log, only writes +//config: from user space. +//config: +//config:config FEATURE_SETCONSOLE_LONG_OPTIONS +//config: bool "Enable long options" +//config: default y +//config: depends on SETCONSOLE && LONG_OPTS + +//applet:IF_SETCONSOLE(APPLET_NOEXEC(setconsole, setconsole, BB_DIR_SBIN, BB_SUID_DROP, setconsole)) + +//kbuild:lib-$(CONFIG_SETCONSOLE) += setconsole.o + +//usage:#define setconsole_trivial_usage +//usage: "[-r] [DEVICE]" +//usage:#define setconsole_full_usage "\n\n" +//usage: "Make writes to /dev/console appear on DEVICE (default: /dev/tty)." +//usage: "\n""Does not redirect kernel log output or reads from /dev/console." +//usage: "\n" +//usage: "\n"" -r Reset: writes to /dev/console go to kernel log tty(s)" + +/* It was a bbox-specific invention, but SUSE does have a similar utility. + * SUSE has no -r option, though. + */ + +#include "libbb.h" + +int setconsole_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int setconsole_main(int argc UNUSED_PARAM, char **argv) +{ + const char *device = CURRENT_TTY; + int reset; + + /* at most one non-option argument */ + reset = getopt32(argv, "^" "r" "\0" "?1"); + + argv += 1 + reset; + if (*argv) { + device = *argv; + } else { + if (reset) + device = DEV_CONSOLE; + } + +//TODO: fails if TIOCCONS redir is already active to some tty. +//I think SUSE version first does TIOCCONS on /dev/console fd (iow: resets) +//then TIOCCONS to new tty? + xioctl(xopen(device, O_WRONLY), TIOCCONS, NULL); + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/setkeycodes.c b/busybox/console-tools/setkeycodes.c new file mode 100644 index 00000000000000..1363ac9d16ff39 --- /dev/null +++ b/busybox/console-tools/setkeycodes.c @@ -0,0 +1,71 @@ +/* vi: set sw=4 ts=4: */ +/* + * setkeycodes + * + * Copyright (C) 1994-1998 Andries E. Brouwer + * + * Adjusted for BusyBox by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config SETKEYCODES +//config: bool "setkeycodes (1.7 kb)" +//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_NOEXEC(setkeycodes, setkeycodes, BB_DIR_USR_BIN, BB_SUID_DROP, setkeycodes)) + +//kbuild:lib-$(CONFIG_SETKEYCODES) += setkeycodes.o + +//usage:#define setkeycodes_trivial_usage +//usage: "{ SCANCODE KEYCODE }..." +//usage:#define setkeycodes_full_usage "\n\n" +//usage: "Modify kernel's scancode-to-keycode map,\n" +//usage: "allowing unusual keyboards to generate usable keycodes.\n\n" +//usage: "SCANCODE is either xx or e0xx (hexadecimal), KEYCODE is decimal." +//usage: +//usage:#define setkeycodes_example_usage +//usage: "$ setkeycodes e030 127\n" + +#include "libbb.h" + +/* From */ +struct kbkeycode { + unsigned scancode, keycode; +}; +enum { + KDSETKEYCODE = 0x4B4D /* write kernel keycode table entry */ +}; + +int setkeycodes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int setkeycodes_main(int argc, char **argv) +{ + int fd; + + if (!(argc & 1) /* if even */ || argc < 2) { + bb_show_usage(); + } + + fd = get_console_fd_or_die(); + + while (argv[1]) { + struct kbkeycode a; + int sc; + + sc = xstrtoul_range(argv[1], 16, 0, 0xe07f); + if (sc >= 0xe000) { + sc -= 0xe000; + sc += 0x0080; + } + a.scancode = sc; + a.keycode = xatou_range(argv[2], 0, 255); + ioctl_or_perror_and_die(fd, KDSETKEYCODE, &a, + "can't set SCANCODE %x to KEYCODE %d", + sc, a.keycode); + argv += 2; + } + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/setlogcons.c b/busybox/console-tools/setlogcons.c new file mode 100644 index 00000000000000..0fad6600aa4d34 --- /dev/null +++ b/busybox/console-tools/setlogcons.c @@ -0,0 +1,66 @@ +/* vi: set sw=4 ts=4: */ +/* + * setlogcons: Send kernel messages to the current console or to console N + * + * Copyright (C) 2006 by Jan Kiszka + * + * Based on setlogcons (kbd-1.12) by Andries E. Brouwer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config SETLOGCONS +//config: bool "setlogcons (1.8 kb)" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: This program redirects the output console of kernel messages. + +//applet:IF_SETLOGCONS(APPLET_NOEXEC(setlogcons, setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP, setlogcons)) + +//kbuild:lib-$(CONFIG_SETLOGCONS) += setlogcons.o + +//usage:#define setlogcons_trivial_usage +//usage: "[N]" +//usage:#define setlogcons_full_usage "\n\n" +//usage: "Pin kernel output to VT console N. Default:0 (do not pin)" + +// Comment from kernel source: +/* ... + * By default, the kernel messages are always printed on the current virtual + * console. However, the user may modify that default with the + * TIOCL_SETKMSGREDIRECT ioctl call. + * + * This function sets the kernel message console to be @new. It returns the old + * virtual console number. The virtual terminal number 0 (both as parameter and + * return value) means no redirection (i.e. always printed on the currently + * active console). + */ + +#include "libbb.h" + +int setlogcons_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int setlogcons_main(int argc UNUSED_PARAM, char **argv) +{ + char *devname; + struct { + char fn; + char subarg; + } arg = { + 11, /* redirect kernel messages (TIOCL_SETKMSGREDIRECT) */ + 0 + }; + + if (argv[1]) + arg.subarg = xatou_range(argv[1], 0, 63); + + /* Can just call it on "/dev/tty1" always, but... + * in my testing, inactive (never opened) VTs are not + * redirected to, despite ioctl not failing. + * + * By using "/dev/ttyN", ensure it is activated. + */ + devname = xasprintf("/dev/tty%u", arg.subarg); + xioctl(xopen(devname, O_RDONLY), TIOCLINUX, &arg); + + return EXIT_SUCCESS; +} diff --git a/busybox/console-tools/showkey.c b/busybox/console-tools/showkey.c new file mode 100644 index 00000000000000..c322ce99d48215 --- /dev/null +++ b/busybox/console-tools/showkey.c @@ -0,0 +1,159 @@ +/* vi: set sw=4 ts=4: */ +/* + * shows keys pressed. inspired by kbd package + * + * Copyright (C) 2008 by Vladimir Dronnikov + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config SHOWKEY +//config: bool "showkey (4.7 kb)" +//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]" +//usage:#define showkey_full_usage "\n\n" +//usage: "Show keys pressed\n" +//usage: "\n -a Display decimal/octal/hex values of the keys" +//usage: "\n -k Display interpreted keycodes (default)" +//usage: "\n -s Display raw scan-codes" + +#include "libbb.h" +#include + + +struct globals { + int kbmode; + struct termios tio, tio0; +}; +#define G (*ptr_to_globals) +#define kbmode (G.kbmode) +#define tio (G.tio) +#define tio0 (G.tio0) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ +} while (0) + + +// set raw tty mode +// also used by microcom +// libbb candidates? +static void xget1(struct termios *t, struct termios *oldt) +{ + tcgetattr(STDIN_FILENO, oldt); + *t = *oldt; + cfmakeraw(t); +} + +static void xset1(struct termios *t) +{ + int ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, t); + if (ret) { + bb_perror_msg("can't tcsetattr for stdin"); + } +} + +int showkey_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int showkey_main(int argc UNUSED_PARAM, char **argv) +{ + enum { + OPT_a = (1<<0), // display the decimal/octal/hex values of the keys + OPT_k = (1<<1), // display only the interpreted keycodes (default) + OPT_s = (1<<2), // display only the raw scan-codes + }; + + INIT_G(); + + // FIXME: aks are all mutually exclusive + getopt32(argv, "aks"); + + // prepare for raw mode + xget1(&tio, &tio0); + // put stdin in raw mode + xset1(&tio); + +#define press_keys "Press any keys, program terminates %s:\r\n\n" + + if (option_mask32 & OPT_a) { + // just read stdin char by char + unsigned char c; + + printf(press_keys, "on EOF (ctrl-D)"); + + // read and show byte values + while (1 == read(STDIN_FILENO, &c, 1)) { + printf("%3u 0%03o 0x%02x\r\n", c, c, c); + if (04 /*CTRL-D*/ == c) + break; + } + } else { + // we assume a PC keyboard + xioctl(STDIN_FILENO, KDGKBMODE, &kbmode); + printf("Keyboard mode was %s.\r\n\n", + kbmode == K_RAW ? "RAW" : + (kbmode == K_XLATE ? "XLATE" : + (kbmode == K_MEDIUMRAW ? "MEDIUMRAW" : + (kbmode == K_UNICODE ? "UNICODE" : "UNKNOWN"))) + ); + + // set raw keyboard mode + xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW)); + + // we should exit on any signal; signals should interrupt read + bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); + + // inform user that program ends after time of inactivity + printf(press_keys, "10s after last keypress"); + + // read and show scancodes + while (!bb_got_signal) { + char buf[18]; + int i, n; + + // setup 10s watchdog + alarm(10); + + // read scancodes + n = read(STDIN_FILENO, buf, sizeof(buf)); + i = 0; + while (i < n) { + if (option_mask32 & OPT_s) { + // show raw scancodes + printf("0x%02x ", buf[i++]); + } else { + // show interpreted scancodes (default) + char c = buf[i]; + int kc; + if (i+2 < n + && (c & 0x7f) == 0 + && (buf[i+1] & 0x80) != 0 + && (buf[i+2] & 0x80) != 0 + ) { + kc = ((buf[i+1] & 0x7f) << 7) | (buf[i+2] & 0x7f); + i += 3; + } else { + kc = (c & 0x7f); + i++; + } + printf("keycode %3u %s", kc, (c & 0x80) ? "release" : "press"); + } + } + puts("\r"); + } + + // restore keyboard mode + xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)kbmode); + } + + // restore console settings + xset1(&tio0); + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/Config.src b/busybox/coreutils/Config.src new file mode 100644 index 00000000000000..1bded03a687aba --- /dev/null +++ b/busybox/coreutils/Config.src @@ -0,0 +1,40 @@ +# +# For a description of the syntax of this configuration file, +# see docs/Kconfig-language.txt. +# + +menu "Coreutils" + +INSERT + +comment "Common options" + +config FEATURE_VERBOSE + bool "Support verbose options (usually -v) for various applets" + default y + help + 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 + +config FEATURE_PRESERVE_HARDLINKS + bool "Preserve hard links" + default y + depends on CP || MV + help + Allow cp and mv to preserve hard links. + +comment "Common options for df, du, ls" + depends on DF || DU || LS + +config FEATURE_HUMAN_READABLE + bool "Support human readable output (example 13k, 23M, 235G)" + default y + depends on DF || DU || LS + help + Allow df, du, and ls to have human readable output. + +endmenu diff --git a/busybox/coreutils/Kbuild.src b/busybox/coreutils/Kbuild.src new file mode 100644 index 00000000000000..a805b64fed65c4 --- /dev/null +++ b/busybox/coreutils/Kbuild.src @@ -0,0 +1,18 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +libs-y += libcoreutils/ + +lib-y:= + +INSERT + +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 addgroup +lib-$(CONFIG_FTPD) += ls.o # used by ftpd diff --git a/busybox/coreutils/basename.c b/busybox/coreutils/basename.c new file mode 100644 index 00000000000000..812a5e63e7aa5b --- /dev/null +++ b/busybox/coreutils/basename.c @@ -0,0 +1,78 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini basename implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Changes: + * 1) Now checks for too many args. Need at least one and at most two. + * 2) Don't check for options, as per SUSv3. + * 3) Save some space by using strcmp(). Calling strncmp() here was silly. + */ +//config:config BASENAME +//config: bool "basename (371 bytes)" +//config: default y +//config: help +//config: basename is used to strip the directory and suffix from filenames, +//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" +//usage: +//usage:#define basename_example_usage +//usage: "$ basename /usr/local/bin/foo\n" +//usage: "foo\n" +//usage: "$ basename /usr/local/bin/\n" +//usage: "bin\n" +//usage: "$ basename /foo/bar.txt .txt\n" +//usage: "bar" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int basename_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int basename_main(int argc UNUSED_PARAM, char **argv) +{ + size_t m, n; + char *s; + + if (argv[1] && strcmp(argv[1], "--") == 0) { + argv++; + } + if (!argv[1]) + bb_show_usage(); + + /* It should strip slash: /abc/def/ -> def */ + s = bb_get_last_path_component_strip(*++argv); + + m = strlen(s); + if (*++argv) { + if (argv[1]) + bb_show_usage(); + n = strlen(*argv); + if ((m > n) && (strcmp(s+m-n, *argv) == 0)) { + m -= n; + /*s[m] = '\0'; - redundant */ + } + } + + /* puts(s) will do, but we can do without stdio this way: */ + s[m++] = '\n'; + /* NB: != is correct here: */ + return full_write(STDOUT_FILENO, s, m) != (ssize_t)m; +} diff --git a/busybox/coreutils/cat.c b/busybox/coreutils/cat.c new file mode 100644 index 00000000000000..fb735f994092d9 --- /dev/null +++ b/busybox/coreutils/cat.c @@ -0,0 +1,215 @@ +/* vi: set sw=4 ts=4: */ +/* + * cat implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config CAT +//config: bool "cat (5.6 kb)" +//config: default y +//config: help +//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. +//config: +//config:config FEATURE_CATN +//config: bool "Enable -n and -b options" +//config: default y +//config: depends on CAT +//config: help +//config: -n numbers all output lines while -b numbers nonempty output lines. +//config: +//config:config FEATURE_CATV +//config: bool "cat -v[etA]" +//config: default y +//config: depends on CAT +//config: help +//config: Display nonprinting characters as escape sequences + +//applet:IF_CAT(APPLET(cat, BB_DIR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_CAT) += cat.o + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */ + +//usage:#if ENABLE_FEATURE_CATN || ENABLE_FEATURE_CATV +//usage:#define cat_trivial_usage +//usage: "[-" IF_FEATURE_CATN("nb") IF_FEATURE_CATV("vteA") "] [FILE]..." +//usage:#else +//usage:#define cat_trivial_usage +//usage: "[FILE]..." +//usage:#endif +//usage:#define cat_full_usage "\n\n" +//usage: "Print FILEs to stdout\n" +//usage: IF_FEATURE_CATN( +//usage: "\n -n Number output lines" +//usage: "\n -b Number nonempty lines" +//usage: ) +//usage: IF_FEATURE_CATV( +//usage: "\n -v Show nonprinting characters as ^x or M-x" +//usage: "\n -t ...and tabs as ^I" +//usage: "\n -e ...and end lines with $" +//usage: "\n -A Same as -vte" +//usage: ) +/* + Longopts not implemented yet: + --number-nonblank number nonempty output lines, overrides -n + --number number all output lines + --show-nonprinting use ^ and M- notation, except for LFD and TAB + --show-all equivalent to -vet + Not implemented yet: + -E, --show-ends display $ at end of each line (-e sans -v) + -T, --show-tabs display TAB characters as ^I (-t sans -v) + -s, --squeeze-blank suppress repeated empty output lines +*/ +//usage: +//usage:#define cat_example_usage +//usage: "$ cat /proc/uptime\n" +//usage: "110716.72 17.67" + +#include "libbb.h" +#include "common_bufsiz.h" + +#if ENABLE_FEATURE_CATV +/* + * cat -v implementation for busybox + * + * Copyright (C) 2006 Rob Landley + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Rob had "cat -v" implemented as a separate applet, catv. + * See "cat -v considered harmful" at + * http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz + * From USENIX Summer Conference Proceedings, 1983 + * """ + * The talk reviews reasons for UNIX's popularity and shows, using UCB cat + * as a primary example, how UNIX has grown fat. cat isn't for printing + * files with line numbers, it isn't for compressing multiple blank lines, + * it's not for looking at non-printing ASCII characters, it's for + * concatenating files. + * We are reminded that ls isn't the place for code to break a single column + * into multiple ones, and that mailnews shouldn't have its own more + * processing or joke encryption code. + * """ + * + * I agree with the argument. Unfortunately, this ship has sailed (1983...). + * There are dozens of Linux distros and each of them has "cat" which supports -v. + * It's unrealistic for us to "reeducate" them to use our, incompatible way + * to achieve "cat -v" effect. The actual effect would be "users pissed off + * by gratuitous incompatibility". + */ +#define CAT_OPT_e (1<<0) +#define CAT_OPT_t (1<<1) +#define CAT_OPT_v (1<<2) +/* -A occupies bit (1<<3) */ +#define CAT_OPT_n ((1<<4) * ENABLE_FEATURE_CATN) +#define CAT_OPT_b ((1<<5) * ENABLE_FEATURE_CATN) +static int catv(unsigned opts, char **argv) +{ + int retval = EXIT_SUCCESS; + int fd; +#if ENABLE_FEATURE_CATN + bool eol_seen = (opts & (CAT_OPT_n|CAT_OPT_b)); + unsigned eol_char = (eol_seen ? '\n' : 0x100); + unsigned skip_num_on = (opts & CAT_OPT_b) ? '\n' : 0x100; + unsigned lineno = 0; +#endif + + BUILD_BUG_ON(CAT_OPT_e != VISIBLE_ENDLINE); + BUILD_BUG_ON(CAT_OPT_t != VISIBLE_SHOW_TABS); +#if 0 /* These consts match, we can just pass "opts" to visible() */ + if (opts & CAT_OPT_e) + flags |= VISIBLE_ENDLINE; + if (opts & CAT_OPT_t) + flags |= VISIBLE_SHOW_TABS; +#endif + +#define read_buf bb_common_bufsiz1 + setup_common_bufsiz(); + do { + fd = open_or_warn_stdin(*argv); + if (fd < 0) { + retval = EXIT_FAILURE; + continue; + } + for (;;) { + int i, res; + + res = read(fd, read_buf, COMMON_BUFSIZE); + if (res < 0) + retval = EXIT_FAILURE; + if (res <= 0) + break; + for (i = 0; i < res; i++) { + unsigned char c = read_buf[i]; + char buf[sizeof("M-^c")]; +#if ENABLE_FEATURE_CATN + if (eol_seen && c != skip_num_on) + printf("%6u ", ++lineno); + eol_seen = (c == eol_char); +#endif + visible(c, buf, opts); + fputs(buf, stdout); + } + } + if (ENABLE_FEATURE_CLEAN_UP && fd) + close(fd); + } while (*++argv); + + fflush_stdout_and_exit(retval); +} +#undef CAT_OPT_n +#undef CAT_OPT_b +#endif + +int cat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cat_main(int argc UNUSED_PARAM, char **argv) +{ +#if ENABLE_FEATURE_CATV || ENABLE_FEATURE_CATN + unsigned opts; + + opts = +#endif + getopt32(argv, IF_FEATURE_CATV("^") + /* -u is ignored ("unbuffered") */ + IF_FEATURE_CATV("etvA")IF_FEATURE_CATN("nb")"u" + IF_FEATURE_CATV("\0" "Aetv" /* -A == -vet */) + ); + argv += optind; + + /* Read from stdin if there's nothing else to do. */ + if (!argv[0]) + *--argv = (char*)"-"; + +#if ENABLE_FEATURE_CATV + if (opts & 7) + return catv(opts, argv); + opts >>= 4; +#endif + +#if ENABLE_FEATURE_CATN +# define CAT_OPT_n (1<<0) +# define CAT_OPT_b (1<<1) + if (opts & (CAT_OPT_n|CAT_OPT_b)) { /* -n or -b */ + struct number_state ns; + + ns.width = 6; + ns.start = 1; + ns.inc = 1; + ns.sep = "\t"; + ns.empty_str = "\n"; + ns.all = !(opts & CAT_OPT_b); /* -n without -b */ + ns.nonempty = (opts & CAT_OPT_b); /* -b (with or without -n) */ + do { + print_numbered_lines(&ns, *argv); + } while (*++argv); + fflush_stdout_and_exit(EXIT_SUCCESS); + } + /*opts >>= 2;*/ +#endif + + return bb_cat(argv); +} diff --git a/busybox/coreutils/chgrp.c b/busybox/coreutils/chgrp.c new file mode 100644 index 00000000000000..27a0edf0deadb1 --- /dev/null +++ b/busybox/coreutils/chgrp.c @@ -0,0 +1,62 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chgrp implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config CHGRP +//config: bool "chgrp (7.2 kb)" +//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. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */ + +//usage:#define chgrp_trivial_usage +//usage: "[-RhLHP"IF_DESKTOP("cvf")"]... GROUP FILE..." +//usage:#define chgrp_full_usage "\n\n" +//usage: "Change the group membership of each FILE to GROUP\n" +//usage: "\n -R Recurse" +//usage: "\n -h Affect symlinks instead of symlink targets" +//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 Verbose" +//usage: "\n -f Hide errors" +//usage: ) +//usage: +//usage:#define chgrp_example_usage +//usage: "$ ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" +//usage: "$ chgrp root /tmp/foo\n" +//usage: "$ ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 andersen root 0 Apr 12 18:25 /tmp/foo\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +int chgrp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chgrp_main(int argc, char **argv) +{ + /* "chgrp [opts] abc file(s)" == "chown [opts] :abc file(s)" */ + char **p = argv; + while (*++p) { + if (p[0][0] != '-') { + p[0] = xasprintf(":%s", p[0]); + break; + } + } + return chown_main(argc, argv); +} diff --git a/busybox/coreutils/chmod.c b/busybox/coreutils/chmod.c new file mode 100644 index 00000000000000..88989bf674b653 --- /dev/null +++ b/busybox/coreutils/chmod.c @@ -0,0 +1,190 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chmod implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Reworked by (C) 2002 Vladimir Oleynik + * to correctly parse '-rwxgoa' + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config CHMOD +//config: bool "chmod (5.1 kb)" +//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. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ + +//usage:#define chmod_trivial_usage +//usage: "[-R"IF_DESKTOP("cvf")"] MODE[,MODE]... FILE..." +//usage:#define chmod_full_usage "\n\n" +//usage: "Each MODE is one or more of the letters ugoa, one of the\n" +//usage: "symbols +-= and one or more of the letters rwxst\n" +//usage: "\n -R Recurse" +//usage: IF_DESKTOP( +//usage: "\n -c List changed files" +//usage: "\n -v List all files" +//usage: "\n -f Hide errors" +//usage: ) +//usage: +//usage:#define chmod_example_usage +//usage: "$ ls -l /tmp/foo\n" +//usage: "-rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" +//usage: "$ chmod u+x /tmp/foo\n" +//usage: "$ ls -l /tmp/foo\n" +//usage: "-rwxrw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo*\n" +//usage: "$ chmod 444 /tmp/foo\n" +//usage: "$ ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +#define OPT_RECURSE (option_mask32 & 1) +#define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0)) +#define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0)) +#define OPT_QUIET (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0)) +#define OPT_STR "R" IF_DESKTOP("vcf") + +/* coreutils: + * chmod never changes the permissions of symbolic links; the chmod + * system call cannot change their permissions. This is not a problem + * since the permissions of symbolic links are never used. + * However, for each symbolic link listed on the command line, chmod changes + * the permissions of the pointed-to file. In contrast, chmod ignores + * symbolic links encountered during recursive directory traversals. + */ + +static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth) +{ + mode_t newmode; + + /* match coreutils behavior */ + if (depth == 0) { + /* statbuf holds lstat result, but we need stat (follow link) */ + if (stat(fileName, statbuf)) + goto err; + } else { /* depth > 0: skip links */ + if (S_ISLNK(statbuf->st_mode)) + return TRUE; + } + + 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) { + if (OPT_VERBOSE + || (OPT_CHANGED && statbuf->st_mode != newmode) + ) { + printf("mode of '%s' changed to %04o (%s)\n", fileName, + newmode & 07777, bb_mode_string(newmode)+1); + } + return TRUE; + } + err: + if (!OPT_QUIET) + bb_simple_perror_msg(fileName); + return FALSE; +} + +int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chmod_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + char *arg, **argp; + char *smode; + + /* Convert first encountered -r into ar, -w into aw etc + * so that getopt would not eat it */ + argp = argv; + while ((arg = *++argp)) { + /* Mode spec must be the first arg (sans -R etc) */ + /* (protect against mishandling e.g. "chmod 644 -r") */ + if (arg[0] != '-') { + arg = NULL; + break; + } + /* An option. Not a -- or valid option? */ + if (arg[1] && !strchr("-"OPT_STR, arg[1])) { + arg[0] = 'a'; + break; + } + } + + /* Parse options */ + getopt32(argv, "^" OPT_STR "\0" "-2"); + argv += optind; + + /* Restore option-like mode if needed */ + if (arg) arg[0] = '-'; + + /* Ok, ready to do the deed now */ + smode = *argv++; + do { + if (!recursive_action(*argv, + OPT_RECURSE, // recurse + fileAction, // file action + fileAction, // dir action + smode, // user data + 0) // depth + ) { + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} + +/* +Security: chmod is too important and too subtle. +This is a test script (busybox chmod versus coreutils). +Run it in empty directory. + +#!/bin/sh +t1="/tmp/busybox chmod" +t2="/usr/bin/chmod" +create() { + rm -rf $1; mkdir $1 + ( + cd $1 || exit 1 + mkdir dir + >up + >file + >dir/file + ln -s dir linkdir + ln -s file linkfile + ln -s ../up dir/up + ) +} +tst() { + (cd test1; $t1 $1) + (cd test2; $t2 $1) + (cd test1; ls -lR) >out1 + (cd test2; ls -lR) >out2 + echo "chmod $1" >out.diff + if ! diff -u out1 out2 >>out.diff; then exit 1; fi + rm out.diff +} +echo "If script produced 'out.diff' file, then at least one testcase failed" +create test1; create test2 +tst "a+w file" +tst "a-w dir" +tst "a+w linkfile" +tst "a-w linkdir" +tst "-R a+w file" +tst "-R a-w dir" +tst "-R a+w linkfile" +tst "-R a-w linkdir" +tst "a-r,a+x linkfile" +*/ diff --git a/busybox/coreutils/chown.c b/busybox/coreutils/chown.c new file mode 100644 index 00000000000000..6429fd03006745 --- /dev/null +++ b/busybox/coreutils/chown.c @@ -0,0 +1,232 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config CHOWN +//config: bool "chown (7.2 kb)" +//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 + +//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: "[-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 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: "\n -c List changed files" +//usage: "\n -v List all files" +//usage: "\n -f Hide errors" +//usage: ) +//usage: +//usage:#define chown_example_usage +//usage: "$ ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" +//usage: "$ chown root /tmp/foo\n" +//usage: "$ ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 root andersen 0 Apr 12 18:25 /tmp/foo\n" +//usage: "$ chown root.root /tmp/foo\n" +//usage: "ls -l /tmp/foo\n" +//usage: "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +#define OPT_STR "Rh" IF_DESKTOP("vcfLHP") +#define BIT_RECURSE 1 +#define OPT_RECURSE (opt & 1) +#define OPT_NODEREF (opt & 2) +#define OPT_VERBOSE (IF_DESKTOP(opt & 0x04) IF_NOT_DESKTOP(0)) +#define OPT_CHANGED (IF_DESKTOP(opt & 0x08) IF_NOT_DESKTOP(0)) +#define OPT_QUIET (IF_DESKTOP(opt & 0x10) IF_NOT_DESKTOP(0)) +/* POSIX options + * -L traverse every symbolic link to a directory encountered + * -H if a command line argument is a symbolic link to a directory, traverse it + * -P do not traverse any symbolic links (default) + * We do not conform to the following: + * "Specifying more than one of -H, -L, and -P is not an error. + * The last option specified shall determine the behavior of the utility." */ +/* -L */ +#define BIT_TRAVERSE 0x20 +#define OPT_TRAVERSE (IF_DESKTOP(opt & BIT_TRAVERSE) IF_NOT_DESKTOP(0)) +/* -H or -L */ +#define BIT_TRAVERSE_TOP (0x20|0x40) +#define OPT_TRAVERSE_TOP (IF_DESKTOP(opt & BIT_TRAVERSE_TOP) IF_NOT_DESKTOP(0)) + +#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS +static const char chown_longopts[] ALIGN1 = + "recursive\0" No_argument "R" + "dereference\0" No_argument "\xff" + "no-dereference\0" No_argument "h" +# if ENABLE_DESKTOP + "changes\0" No_argument "c" + "silent\0" No_argument "f" + "quiet\0" No_argument "f" + "verbose\0" No_argument "v" +# endif + ; +#endif + +typedef int (*chown_fptr)(const char *, uid_t, gid_t); + +struct param_t { + struct bb_uidgid_t ugid; + chown_fptr chown_func; +}; + +static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, + void *vparam, int depth UNUSED_PARAM) +{ +#define param (*(struct param_t*)vparam) +#define opt option_mask32 + uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid; + gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid; + + if (param.chown_func(fileName, u, g) == 0) { + if (OPT_VERBOSE + || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g)) + ) { + printf("changed ownership of '%s' to %u:%u\n", + fileName, (unsigned)u, (unsigned)g); + } + return TRUE; + } + if (!OPT_QUIET) + bb_simple_perror_msg(fileName); + return FALSE; +#undef opt +#undef param +} + +int chown_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + int opt, flags; + struct param_t param; + +#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS + opt = getopt32long(argv, "^" OPT_STR "\0" "-2", chown_longopts); +#else + opt = getopt32(argv, "^" OPT_STR "\0" "-2"); +#endif + argv += optind; + + /* This matches coreutils behavior (almost - see below) */ + param.chown_func = chown; + if (OPT_NODEREF + /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */ + IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE) + ) { + param.chown_func = lchown; + } + + flags = ACTION_DEPTHFIRST; /* match coreutils order */ + if (OPT_RECURSE) + flags |= ACTION_RECURSE; + if (OPT_TRAVERSE_TOP) + flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */ + if (OPT_TRAVERSE) + flags |= ACTION_FOLLOWLINKS; /* follow links if -L */ + + parse_chown_usergroup_or_die(¶m.ugid, argv[0]); + + /* Ok, ready to do the deed now */ + while (*++argv) { + if (!recursive_action(*argv, + flags, /* flags */ + fileAction, /* file action */ + fileAction, /* dir action */ + ¶m, /* user data */ + 0) /* depth */ + ) { + retval = EXIT_FAILURE; + } + } + + return retval; +} + +/* +Testcase. Run in empty directory. + +#!/bin/sh +t1="/tmp/busybox chown" +t2="/usr/bin/chown" +create() { + rm -rf $1; mkdir $1 + ( + cd $1 || exit 1 + mkdir dir dir2 + >up + >file + >dir/file + >dir2/file + ln -s dir linkdir + ln -s file linkfile + ln -s ../up dir/linkup + ln -s ../dir2 dir/linkupdir2 + ) + chown -R 0:0 $1 +} +tst() { + create test1 + create test2 + echo "[$1]" >>test1.out + echo "[$1]" >>test2.out + (cd test1; $t1 $1) >>test1.out 2>&1 + (cd test2; $t2 $1) >>test2.out 2>&1 + (cd test1; ls -lnR) >out1 + (cd test2; ls -lnR) >out2 + echo "chown $1" >out.diff + if ! diff -u out1 out2 >>out.diff; then exit 1; fi + rm out.diff +} +tst_for_each() { + tst "$1 1:1 file" + tst "$1 1:1 dir" + tst "$1 1:1 linkdir" + tst "$1 1:1 linkfile" +} +echo "If script produced 'out.diff' file, then at least one testcase failed" +>test1.out +>test2.out +# These match coreutils 6.8: +tst_for_each "-v" +tst_for_each "-vR" +tst_for_each "-vRP" +tst_for_each "-vRL" +tst_for_each "-vRH" +tst_for_each "-vh" +tst_for_each "-vhR" +tst_for_each "-vhRP" +tst_for_each "-vhRL" +tst_for_each "-vhRH" +# Fix `name' in coreutils output +sed 's/`/'"'"'/g' -i test2.out +# Compare us with coreutils output +diff -u test1.out test2.out + +*/ diff --git a/busybox/coreutils/chroot.c b/busybox/coreutils/chroot.c new file mode 100644 index 00000000000000..78751df84c30ff --- /dev/null +++ b/busybox/coreutils/chroot.c @@ -0,0 +1,55 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chroot implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config CHROOT +//config: bool "chroot (3.7 kb)" +//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_NOEXEC(chroot, chroot, BB_DIR_USR_SBIN, BB_SUID_DROP, chroot)) + +//kbuild:lib-$(CONFIG_CHROOT) += chroot.o + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +//usage:#define chroot_trivial_usage +//usage: "NEWROOT [PROG ARGS]" +//usage:#define chroot_full_usage "\n\n" +//usage: "Run PROG with root directory set to NEWROOT" +//usage: +//usage:#define chroot_example_usage +//usage: "$ ls -l /bin/ls\n" +//usage: "lrwxrwxrwx 1 root root 12 Apr 13 00:46 /bin/ls -> /BusyBox\n" +//usage: "# mount /dev/hdc1 /mnt -t minix\n" +//usage: "# chroot /mnt\n" +//usage: "# ls -l /bin/ls\n" +//usage: "-rwxr-xr-x 1 root root 40816 Feb 5 07:45 /bin/ls*\n" + +#include "libbb.h" + +int chroot_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chroot_main(int argc UNUSED_PARAM, char **argv) +{ + ++argv; + if (!*argv) + bb_show_usage(); + + xchroot(*argv); + + ++argv; + if (!*argv) { /* no 2nd param (PROG), use shell */ + argv -= 2; + argv[0] = (char *) get_shell_name(); + argv[1] = (char *) "-i"; /* GNU coreutils 8.4 compat */ + /*argv[2] = NULL; - already is */ + } + + BB_EXECVP_or_die(argv); +} diff --git a/busybox/coreutils/cksum.c b/busybox/coreutils/cksum.c new file mode 100644 index 00000000000000..e46e249f29588c --- /dev/null +++ b/busybox/coreutils/cksum.c @@ -0,0 +1,84 @@ +/* vi: set sw=4 ts=4: */ +/* + * cksum - calculate the CRC32 checksum of a file + * + * Copyright (C) 2006 by Rob Sullivan, with ideas from code by Walter Harms + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config CKSUM +//config: bool "cksum (4.2 kb)" +//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)) +/* bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed. */ + +//kbuild:lib-$(CONFIG_CKSUM) += cksum.o + +//usage:#define cksum_trivial_usage +//usage: "FILE..." +//usage:#define cksum_full_usage "\n\n" +//usage: "Calculate the CRC32 checksums of FILEs" + +#include "libbb.h" +#include "common_bufsiz.h" + +/* This is a NOEXEC applet. Be very careful! */ + +int cksum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cksum_main(int argc UNUSED_PARAM, char **argv) +{ + uint32_t *crc32_table = crc32_filltable(NULL, 1); + int exit_code = EXIT_SUCCESS; + +#if ENABLE_DESKTOP + getopt32(argv, ""); /* coreutils 6.9 compat */ + argv += optind; +#else + argv++; +#endif + + setup_common_bufsiz(); + do { + uint32_t crc; + off_t filesize; + int fd = open_or_warn_stdin(*argv ? *argv : bb_msg_standard_input); + + if (fd < 0) { + exit_code = EXIT_FAILURE; + continue; + } + + crc = 0; + filesize = 0; +#define read_buf bb_common_bufsiz1 + for (;;) { + uoff_t t; + int bytes_read = safe_read(fd, read_buf, COMMON_BUFSIZE); + if (bytes_read > 0) { + filesize += bytes_read; + } else { + /* Checksum filesize bytes, LSB first, and exit */ + close(fd); + fd = -1; /* break flag */ + t = filesize; + bytes_read = 0; + while (t != 0) { + read_buf[bytes_read++] = (uint8_t)t; + t >>= 8; + } + } + crc = crc32_block_endian1(crc, read_buf, bytes_read, crc32_table); + if (fd < 0) + break; + } + + crc = ~crc; + printf((*argv ? "%u %"OFF_FMT"u %s\n" : "%u %"OFF_FMT"u\n"), + (unsigned)crc, filesize, *argv); + } while (*argv && *++argv); + + fflush_stdout_and_exit(exit_code); +} diff --git a/busybox/coreutils/comm.c b/busybox/coreutils/comm.c new file mode 100644 index 00000000000000..4bee7767752edc --- /dev/null +++ b/busybox/coreutils/comm.c @@ -0,0 +1,117 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini comm implementation for busybox + * + * Copyright (C) 2005 by Robert Sullivan + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config COMM +//config: bool "comm (3.9 kb)" +//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" +//usage:#define comm_full_usage "\n\n" +//usage: "Compare FILE1 with FILE2\n" +//usage: "\n -1 Suppress lines unique to FILE1" +//usage: "\n -2 Suppress lines unique to FILE2" +//usage: "\n -3 Suppress lines common to both files" + +#include "libbb.h" + +#define COMM_OPT_1 (1 << 0) +#define COMM_OPT_2 (1 << 1) +#define COMM_OPT_3 (1 << 2) + +/* writeline outputs the input given, appropriately aligned according to class */ +static void writeline(char *line, int class) +{ + int flags = option_mask32; + if (class == 0) { + if (flags & COMM_OPT_1) + return; + } else if (class == 1) { + if (flags & COMM_OPT_2) + return; + if (!(flags & COMM_OPT_1)) + putchar('\t'); + } else /*if (class == 2)*/ { + if (flags & COMM_OPT_3) + return; + if (!(flags & COMM_OPT_1)) + putchar('\t'); + if (!(flags & COMM_OPT_2)) + putchar('\t'); + } + puts(line); +} + +int comm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int comm_main(int argc UNUSED_PARAM, char **argv) +{ + char *thisline[2]; + FILE *stream[2]; + int i; + int order; + + getopt32(argv, "^" "123" "\0" "=2"); + argv += optind; + + for (i = 0; i < 2; ++i) { + stream[i] = xfopen_stdin(argv[i]); + } + + order = 0; + thisline[1] = thisline[0] = NULL; + while (1) { + if (order <= 0) { + free(thisline[0]); + thisline[0] = xmalloc_fgetline(stream[0]); + } + if (order >= 0) { + free(thisline[1]); + thisline[1] = xmalloc_fgetline(stream[1]); + } + + i = !thisline[0] + (!thisline[1] << 1); + if (i) + break; + order = strcmp(thisline[0], thisline[1]); + + if (order >= 0) + writeline(thisline[1], order ? 1 : 2); + else + writeline(thisline[0], 0); + } + + /* EOF at least on one of the streams */ + i &= 1; + if (thisline[i]) { + /* stream[i] is not at EOF yet */ + /* we did not print thisline[i] yet */ + char *p = thisline[i]; + writeline(p, i); + while (1) { + free(p); + p = xmalloc_fgetline(stream[i]); + if (!p) + break; + writeline(p, i); + } + } + + if (ENABLE_FEATURE_CLEAN_UP) { + fclose(stream[0]); + fclose(stream[1]); + } + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/cp.c b/busybox/coreutils/cp.c new file mode 100644 index 00000000000000..455bffbba1299f --- /dev/null +++ b/busybox/coreutils/cp.c @@ -0,0 +1,248 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cp implementation for busybox + * + * Copyright (C) 2000 by Matt Kraai + * SELinux support by Yuichi Nakamura + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. + */ +//config:config CP +//config: bool "cp (9.7 kb)" +//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" +//config: default y +//config: depends on CP && LONG_OPTS +//config: help +//config: Enable long options. +//config: Also add support for --parents option. + +//applet:IF_CP(APPLET_NOEXEC(cp, cp, BB_DIR_BIN, BB_SUID_DROP, cp)) +/* NOEXEC despite cases when it can be a "runner" (cp -r LARGE_DIR NEW_DIR) */ + +//kbuild:lib-$(CONFIG_CP) += cp.o + +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */ + +//usage:#define cp_trivial_usage +//usage: "[OPTIONS] SOURCE... DEST" +//usage:#define cp_full_usage "\n\n" +//usage: "Copy SOURCE(s) to DEST\n" +//usage: "\n -a Same as -dpR" +//usage: IF_SELINUX( +//usage: "\n -c Preserve security context" +//usage: ) +//usage: "\n -R,-r Recurse" +//usage: "\n -d,-P Preserve symlinks (default if -R)" +//usage: "\n -L Follow all symlinks" +//usage: "\n -H Follow symlinks on command line" +//usage: "\n -p Preserve file attributes if possible" +//usage: "\n -f Overwrite" +//usage: "\n -i Prompt before overwrite" +//usage: "\n -l,-s Create (sym)links" +//usage: "\n -T Treat DEST as a normal file" +//usage: "\n -u Copy only newer files" + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +/* This is a NOEXEC applet. Be very careful! */ + +int cp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cp_main(int argc, char **argv) +{ + struct stat source_stat; + struct stat dest_stat; + const char *last; + const char *dest; + int s_flags; + int d_flags; + int flags; + int status; + enum { + FILEUTILS_CP_OPTNUM = sizeof(FILEUTILS_CP_OPTSTR)-1, +#if ENABLE_FEATURE_CP_LONG_OPTIONS + /*OPT_rmdest = FILEUTILS_RMDEST = 1 << FILEUTILS_CP_OPTNUM */ + OPT_parents = 1 << (FILEUTILS_CP_OPTNUM+1), +#endif + }; + +#if ENABLE_FEATURE_CP_LONG_OPTIONS + flags = getopt32long(argv, "^" + FILEUTILS_CP_OPTSTR + "\0" + // Need at least two arguments + // Soft- and hardlinking doesn't mix + // -P and -d are the same (-P is POSIX, -d is GNU) + // -r and -R are the same + // -R (and therefore -r) turns on -d (coreutils does this) + // -a = -pdR + "-2:l--s:s--l:Pd:rRd:Rd:apdR", + "archive\0" No_argument "a" + "force\0" No_argument "f" + "interactive\0" No_argument "i" + "link\0" No_argument "l" + "dereference\0" No_argument "L" + "no-dereference\0" No_argument "P" + "recursive\0" No_argument "R" + "symbolic-link\0" No_argument "s" + "no-target-directory\0" No_argument "T" + "verbose\0" No_argument "v" + "update\0" No_argument "u" + "remove-destination\0" No_argument "\xff" + "parents\0" No_argument "\xfe" + ); +#else + flags = getopt32(argv, "^" + FILEUTILS_CP_OPTSTR + "\0" + "-2:l--s:s--l:Pd:rRd:Rd:apdR" + ); +#endif + /* Options of cp from GNU coreutils 6.10: + * -a, --archive + * -f, --force + * -i, --interactive + * -l, --link + * -L, --dereference + * -P, --no-dereference + * -R, -r, --recursive + * -s, --symbolic-link + * -v, --verbose + * -H follow command-line symbolic links in SOURCE + * -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 + * -T, --no-target-directory + * treat DEST as a normal file + * NOT SUPPORTED IN BBOX: + * --backup[=CONTROL] + * make a backup of each existing destination file + * -b like --backup but does not accept an argument + * --copy-contents + * copy contents of special files when recursive + * --preserve[=ATTR_LIST] + * preserve attributes (default: mode,ownership,timestamps), + * if possible additional attributes: security context,links,all + * --no-preserve=ATTR_LIST + * --sparse=WHEN + * control creation of sparse files + * --strip-trailing-slashes + * remove any trailing slashes from each SOURCE argument + * -S, --suffix=SUFFIX + * override the usual backup suffix + * -t, --target-directory=DIRECTORY + * copy all SOURCE arguments into DIRECTORY + * -x, --one-file-system + * stay on this file system + * -Z, --context=CONTEXT + * (SELinux) set SELinux security context of copy to CONTEXT + */ + argc -= optind; + argv += optind; + /* Reverse this bit. If there is -d, bit is not set: */ + flags ^= FILEUTILS_DEREFERENCE; + /* coreutils 6.9 compat: + * by default, "cp" derefs symlinks (creates regular dest files), + * but "cp -R" does not. We switch off deref if -r or -R (see above). + * However, "cp -RL" must still deref symlinks: */ + if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */ + flags |= FILEUTILS_DEREFERENCE; + +#if ENABLE_SELINUX + if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) { + selinux_or_die(); + } +#endif + + status = EXIT_SUCCESS; + last = argv[argc - 1]; + /* If there are only two arguments and... */ + if (argc == 2) { + s_flags = cp_mv_stat2(*argv, &source_stat, + (flags & FILEUTILS_DEREFERENCE) ? stat : lstat); + if (s_flags < 0) + return EXIT_FAILURE; + d_flags = cp_mv_stat(last, &dest_stat); + if (d_flags < 0) + return EXIT_FAILURE; + + if (flags & FILEUTILS_NO_TARGET_DIR) { /* -T */ + if (!(s_flags & 2) && (d_flags & 2)) + /* cp -T NOTDIR DIR */ + bb_error_msg_and_die("'%s' is a directory", last); + } + +#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... */ + if (!((s_flags | d_flags) & 2) + /* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */ + || ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) + || (flags & FILEUTILS_NO_TARGET_DIR) + ) { + /* Do a simple copy */ + dest = last; + goto DO_COPY; /* NB: argc==2 -> *++argv==last */ + } + } else if (flags & FILEUTILS_NO_TARGET_DIR) { + bb_error_msg_and_die("too many arguments"); + } + + while (1) { +#if ENABLE_FEATURE_CP_LONG_OPTIONS + if (flags & OPT_parents) { + char *dest_dup; + char *dest_dir; + dest = concat_path_file(last, *argv); + dest_dup = xstrdup(dest); + dest_dir = dirname(dest_dup); + if (bb_make_directory(dest_dir, -1, FILEUTILS_RECUR)) { + return EXIT_FAILURE; + } + free(dest_dup); + goto DO_COPY; + } +#endif + dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); + DO_COPY: + if (copy_file(*argv, dest, flags) < 0) { + status = EXIT_FAILURE; + } + if (*++argv == last) { + /* possibly leaking dest... */ + break; + } + /* don't move up: dest may be == last and not malloced! */ + free((void*)dest); + } + + /* Exit. We are NOEXEC, not NOFORK. We do exit at the end of main() */ + return status; +} diff --git a/busybox/coreutils/cut.c b/busybox/coreutils/cut.c new file mode 100644 index 00000000000000..cdd90ab44c01b9 --- /dev/null +++ b/busybox/coreutils/cut.c @@ -0,0 +1,319 @@ +/* vi: set sw=4 ts=4: */ +/* + * cut.c - minimalist version of cut + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley + * debloated by Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config CUT +//config: bool "cut (5.3 kb)" +//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]..." +//usage:#define cut_full_usage "\n\n" +//usage: "Print selected fields from each input FILE to stdout\n" +//usage: "\n -b LIST Output only bytes from LIST" +//usage: "\n -c LIST Output only characters from LIST" +//usage: "\n -d CHAR Use CHAR instead of tab as the field delimiter" +//usage: "\n -s Output only the lines containing delimiter" +//usage: "\n -f N Print only these fields" +//usage: "\n -n Ignored" +//usage: +//usage:#define cut_example_usage +//usage: "$ echo \"Hello world\" | cut -f 1 -d ' '\n" +//usage: "Hello\n" +//usage: "$ echo \"Hello world\" | cut -f 2 -d ' '\n" +//usage: "world\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +/* option vars */ +#define OPT_STR "b:c:f:d:sn" +#define CUT_OPT_BYTE_FLGS (1 << 0) +#define CUT_OPT_CHAR_FLGS (1 << 1) +#define CUT_OPT_FIELDS_FLGS (1 << 2) +#define CUT_OPT_DELIM_FLGS (1 << 3) +#define CUT_OPT_SUPPRESS_FLGS (1 << 4) + +struct cut_list { + int startpos; + int endpos; +}; + +enum { + BOL = 0, + EOL = INT_MAX, + NON_RANGE = -1 +}; + +static int cmpfunc(const void *a, const void *b) +{ + return (((struct cut_list *) a)->startpos - + ((struct cut_list *) b)->startpos); +} + +static void cut_file(FILE *file, char delim, const struct cut_list *cut_lists, unsigned nlists) +{ + char *line; + unsigned linenum = 0; /* keep these zero-based to be consistent */ + + /* go through every line in the file */ + while ((line = xmalloc_fgetline(file)) != NULL) { + + /* set up a list so we can keep track of what's been printed */ + int linelen = strlen(line); + char *printed = xzalloc(linelen + 1); + char *orig_line = line; + unsigned cl_pos = 0; + int spos; + + /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ + if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) { + /* print the chars specified in each cut list */ + for (; cl_pos < nlists; cl_pos++) { + spos = cut_lists[cl_pos].startpos; + while (spos < linelen) { + if (!printed[spos]) { + printed[spos] = 'X'; + putchar(line[spos]); + } + spos++; + if (spos > cut_lists[cl_pos].endpos + /* NON_RANGE is -1, so if below is true, + * the above was true too (spos is >= 0) */ + /* || cut_lists[cl_pos].endpos == NON_RANGE */ + ) { + break; + } + } + } + } else if (delim == '\n') { /* cut by lines */ + spos = cut_lists[cl_pos].startpos; + + /* get out if we have no more lists to process or if the lines + * are lower than what we're interested in */ + if (((int)linenum < spos) || (cl_pos >= nlists)) + goto next_line; + + /* if the line we're looking for is lower than the one we were + * passed, it means we displayed it already, so move on */ + while (spos < (int)linenum) { + spos++; + /* go to the next list if we're at the end of this one */ + if (spos > cut_lists[cl_pos].endpos + || cut_lists[cl_pos].endpos == NON_RANGE + ) { + cl_pos++; + /* get out if there's no more lists to process */ + if (cl_pos >= nlists) + goto next_line; + spos = cut_lists[cl_pos].startpos; + /* get out if the current line is lower than the one + * we just became interested in */ + if ((int)linenum < spos) + goto next_line; + } + } + + /* If we made it here, it means we've found the line we're + * looking for, so print it */ + puts(line); + goto next_line; + } else { /* cut by fields */ + int ndelim = -1; /* zero-based / one-based problem */ + int nfields_printed = 0; + char *field = NULL; + char delimiter[2]; + + delimiter[0] = delim; + delimiter[1] = 0; + + /* does this line contain any delimiters? */ + if (strchr(line, delim) == NULL) { + if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS)) + puts(line); + goto next_line; + } + + /* process each list on this line, for as long as we've got + * a line to process */ + for (; cl_pos < nlists && line; cl_pos++) { + spos = cut_lists[cl_pos].startpos; + do { + /* find the field we're looking for */ + while (line && ndelim < spos) { + field = strsep(&line, delimiter); + ndelim++; + } + + /* we found it, and it hasn't been printed yet */ + if (field && ndelim == spos && !printed[ndelim]) { + /* if this isn't our first time through, we need to + * print the delimiter after the last field that was + * printed */ + if (nfields_printed > 0) + putchar(delim); + fputs(field, stdout); + printed[ndelim] = 'X'; + nfields_printed++; /* shouldn't overflow.. */ + } + + spos++; + + /* keep going as long as we have a line to work with, + * this is a list, and we're not at the end of that + * list */ + } while (spos <= cut_lists[cl_pos].endpos && line + && cut_lists[cl_pos].endpos != NON_RANGE); + } + } + /* if we printed anything at all, we need to finish it with a + * newline cuz we were handed a chomped line */ + putchar('\n'); + next_line: + linenum++; + free(printed); + free(orig_line); + } +} + +int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cut_main(int argc UNUSED_PARAM, char **argv) +{ + /* growable array holding a series of lists */ + struct cut_list *cut_lists = NULL; + unsigned nlists = 0; /* number of elements in above list */ + char delim = '\t'; /* delimiter, default is tab */ + char *sopt, *ltok; + unsigned opt; + + opt = getopt32(argv, "^" + OPT_STR + "\0" "b--bcf:c--bcf:f--bcf", + &sopt, &sopt, &sopt, <ok + ); +// argc -= optind; + argv += optind; + if (!(opt & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS))) + bb_error_msg_and_die("expected a list of bytes, characters, or fields"); + + if (opt & CUT_OPT_DELIM_FLGS) { + if (ltok[0] && ltok[1]) { /* more than 1 char? */ + bb_error_msg_and_die("the delimiter must be a single character"); + } + delim = ltok[0]; + } + + /* non-field (char or byte) cutting has some special handling */ + if (!(opt & CUT_OPT_FIELDS_FLGS)) { + static const char _op_on_field[] ALIGN1 = " only when operating on fields"; + + if (opt & CUT_OPT_SUPPRESS_FLGS) { + bb_error_msg_and_die + ("suppressing non-delimited lines makes sense%s", + _op_on_field); + } + if (delim != '\t') { + bb_error_msg_and_die + ("a delimiter may be specified%s", _op_on_field); + } + } + + /* + * parse list and put values into startpos and endpos. + * valid list formats: N, N-, N-M, -M + * more than one list can be separated by commas + */ + { + char *ntok; + int s = 0, e = 0; + + /* take apart the lists, one by one (they are separated with commas) */ + while ((ltok = strsep(&sopt, ",")) != NULL) { + + /* it's actually legal to pass an empty list */ + if (!ltok[0]) + continue; + + /* get the start pos */ + ntok = strsep(<ok, "-"); + if (!ntok[0]) { + s = BOL; + } else { + s = xatoi_positive(ntok); + /* account for the fact that arrays are zero based, while + * the user expects the first char on the line to be char #1 */ + if (s != 0) + s--; + } + + /* get the end pos */ + if (ltok == NULL) { + e = NON_RANGE; + } else if (!ltok[0]) { + e = EOL; + } else { + e = xatoi_positive(ltok); + /* if the user specified and end position of 0, + * that means "til the end of the line" */ + if (e == 0) + e = EOL; + e--; /* again, arrays are zero based, lines are 1 based */ + if (e == s) + e = NON_RANGE; + } + + /* add the new list */ + cut_lists = xrealloc_vector(cut_lists, 4, nlists); + /* NB: startpos is always >= 0, + * while endpos may be = NON_RANGE (-1) */ + cut_lists[nlists].startpos = s; + cut_lists[nlists].endpos = e; + nlists++; + } + + /* make sure we got some cut positions out of all that */ + if (nlists == 0) + bb_error_msg_and_die("missing list of positions"); + + /* now that the lists are parsed, we need to sort them to make life + * easier on us when it comes time to print the chars / fields / lines + */ + qsort(cut_lists, nlists, sizeof(cut_lists[0]), cmpfunc); + } + + { + int retval = EXIT_SUCCESS; + + if (!*argv) + *--argv = (char *)"-"; + + do { + FILE *file = fopen_or_warn_stdin(*argv); + if (!file) { + retval = EXIT_FAILURE; + continue; + } + cut_file(file, delim, cut_lists, nlists); + fclose_if_not_stdin(file); + } while (*++argv); + + if (ENABLE_FEATURE_CLEAN_UP) + free(cut_lists); + fflush_stdout_and_exit(retval); + } +} diff --git a/busybox/coreutils/date.c b/busybox/coreutils/date.c new file mode 100644 index 00000000000000..87dc3bbd0cf6c5 --- /dev/null +++ b/busybox/coreutils/date.c @@ -0,0 +1,396 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini date implementation for busybox + * + * by Matthew Grant + * + * iso-format handling added by Robert Griebl + * 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 + an RFC 2822 compliant date output for shell scripting + mail commands */ + +/* Input parsing code is always bulky - used heavy duty libc stuff as + much as possible, missed out a lot of bounds checking */ + +//config:config DATE +//config: bool "date (7.1 kb)" +//config: default y +//config: help +//config: date is used to set the system date or display the +//config: current time in the given format. +//config: +//config:config FEATURE_DATE_ISOFMT +//config: bool "Enable ISO date format output (-I)" +//config: default y +//config: depends on DATE +//config: help +//config: Enable option (-I) to output an ISO-8601 compliant +//config: date/time string. +//config: +//config:# defaults to "no": stat's nanosecond field is a bit non-portable +//config:config FEATURE_DATE_NANO +//config: bool "Support %[num]N nanosecond format specifier" +//config: default n # syscall(__NR_clock_gettime) +//config: depends on DATE +//config: select PLATFORM_LINUX +//config: help +//config: Support %[num]N format specifier. Adds ~250 bytes of code. +//config: +//config:config FEATURE_DATE_COMPAT +//config: bool "Support weird 'date MMDDhhmm[[YY]YY][.ss]' format" +//config: default y +//config: depends on DATE +//config: help +//config: System time can be set by 'date -s DATE' and simply 'date DATE', +//config: but formats of DATE string are different. 'date DATE' accepts +//config: a rather weird MMDDhhmm[[YY]YY][.ss] format with completely +//config: unnatural placement of year between minutes and seconds. +//config: date -s (and other commands like touch -d) use more sensible +//config: formats (for one, ISO format YYYY-MM-DD hh:mm:ss.ssssss). +//config: +//config: With this option off, 'date DATE' is 'date -s DATE' support +//config: the same format. With it on, 'date DATE' additionally supports +//config: MMDDhhmm[[YY]YY][.ss] format. + +//applet:IF_DATE(APPLET_NOEXEC(date, date, BB_DIR_BIN, BB_SUID_DROP, date)) +/* bb_common_bufsiz1 usage here is safe wrt NOEXEC: not expecting it to be zeroed. */ + +//kbuild:lib-$(CONFIG_DATE) += date.o + +/* GNU coreutils 6.9 man page: + * date [OPTION]... [+FORMAT] + * date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]] + * -d, --date=STRING + * display time described by STRING, not 'now' + * -f, --file=DATEFILE + * like --date once for each line of DATEFILE + * -r, --reference=FILE + * display the last modification time of FILE + * -R, --rfc-2822 + * output date and time in RFC 2822 format. + * Example: Mon, 07 Aug 2006 12:34:56 -0600 + * --rfc-3339=TIMESPEC + * output date and time in RFC 3339 format. + * TIMESPEC='date', 'seconds', or 'ns' + * Date and time components are separated by a single space: + * 2006-08-07 12:34:56-06:00 + * -s, --set=STRING + * set time described by STRING + * -u, --utc, --universal + * print or set Coordinated Universal Time + * + * Busybox: + * long options are not supported + * -f is not supported + * -I seems to roughly match --rfc-3339, but -I has _optional_ param + * (thus "-I seconds" doesn't work, only "-Iseconds"), + * and does not support -Ins + * -D FMT is a bbox extension for _input_ conversion of -d DATE + */ + +//usage:#define date_trivial_usage +//usage: "[OPTIONS] [+FMT] [TIME]" +//usage:#define date_full_usage "\n\n" +//usage: "Display time (using +FMT), or set time\n" +//usage: IF_NOT_LONG_OPTS( +//usage: "\n [-s] TIME Set time to TIME" +//usage: "\n -u Work in UTC (don't convert to local time)" +//usage: "\n -R Output RFC-2822 compliant date string" +//usage: ) IF_LONG_OPTS( +//usage: "\n [-s,--set] TIME Set time to TIME" +//usage: "\n -u,--utc Work in UTC (don't convert to local time)" +//usage: "\n -R,--rfc-2822 Output RFC-2822 compliant date string" +//usage: ) +//usage: IF_FEATURE_DATE_ISOFMT( +//usage: "\n -I[SPEC] Output ISO-8601 compliant date string" +//usage: "\n SPEC='date' (default) for date only," +//usage: "\n 'hours', 'minutes', or 'seconds' for date and" +//usage: "\n time to the indicated precision" +//usage: ) +//usage: IF_NOT_LONG_OPTS( +//usage: "\n -r FILE Display last modification time of FILE" +//usage: "\n -d TIME Display TIME, not 'now'" +//usage: ) IF_LONG_OPTS( +//usage: "\n -r,--reference FILE Display last modification time of FILE" +//usage: "\n -d,--date TIME Display TIME, not 'now'" +//usage: ) +//usage: IF_FEATURE_DATE_ISOFMT( +//usage: "\n -D FMT Use FMT for -d TIME conversion" +//usage: ) +//usage: "\n" +//usage: "\nRecognized TIME formats:" +//usage: "\n hh:mm[:ss]" +//usage: "\n [YYYY.]MM.DD-hh:mm[:ss]" +//usage: "\n YYYY-MM-DD hh:mm[:ss]" +//usage: "\n [[[[[YY]YY]MM]DD]hh]mm[.ss]" +//usage: IF_FEATURE_DATE_COMPAT( +//usage: "\n 'date TIME' form accepts MMDDhhmm[[YY]YY][.ss] instead" +//usage: ) +//usage: +//usage:#define date_example_usage +//usage: "$ date\n" +//usage: "Wed Apr 12 18:52:41 MDT 2000\n" + +#include "libbb.h" +#include "common_bufsiz.h" +#if ENABLE_FEATURE_DATE_NANO +# include +#endif + +enum { + OPT_RFC2822 = (1 << 0), /* R */ + OPT_SET = (1 << 1), /* s */ + OPT_UTC = (1 << 2), /* u */ + OPT_DATE = (1 << 3), /* d */ + OPT_REFERENCE = (1 << 4), /* r */ + OPT_TIMESPEC = (1 << 5) * ENABLE_FEATURE_DATE_ISOFMT, /* I */ + OPT_HINT = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */ +}; + +#if ENABLE_LONG_OPTS +static const char date_longopts[] ALIGN1 = + "rfc-822\0" No_argument "R" + "rfc-2822\0" No_argument "R" + "set\0" Required_argument "s" + "utc\0" No_argument "u" + /* "universal\0" No_argument "u" */ + "date\0" Required_argument "d" + "reference\0" Required_argument "r" + ; +#endif + +/* We are a NOEXEC applet. + * Obstacles to NOFORK: + * - we change env + * - xasprintf result not freed + * - after xasprintf we use other xfuncs + */ + +static void maybe_set_utc(int opt) +{ + if (opt & OPT_UTC) + putenv((char*)"TZ=UTC0"); +} + +int date_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int date_main(int argc UNUSED_PARAM, char **argv) +{ + struct timespec ts; + struct tm tm_time; + char buf_fmt_dt2str[64]; + unsigned opt; + int ifmt = -1; + char *date_str; + char *fmt_dt2str; + char *fmt_str2dt; + char *filename; + char *isofmt_arg = NULL; + + opt = getopt32long(argv, "^" + "Rs:ud:r:" + IF_FEATURE_DATE_ISOFMT("I::D:") + "\0" + "d--s:s--d" + IF_FEATURE_DATE_ISOFMT(":R--I:I--R"), + date_longopts, + &date_str, &date_str, &filename + IF_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt) + ); + argv += optind; + + maybe_set_utc(opt); + + if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_TIMESPEC)) { + ifmt = 0; /* default is date */ + if (isofmt_arg) { + static const char isoformats[] ALIGN1 = + "date\0""hours\0""minutes\0""seconds\0"; /* ns? */ + ifmt = index_in_substrings(isoformats, isofmt_arg); + if (ifmt < 0) + bb_show_usage(); + } + } + + fmt_dt2str = NULL; + if (argv[0] && argv[0][0] == '+') { + fmt_dt2str = &argv[0][1]; /* skip over the '+' */ + argv++; + } + if (!(opt & (OPT_SET | OPT_DATE))) { + opt |= OPT_SET; + date_str = argv[0]; /* can be NULL */ + if (date_str) { +#if ENABLE_FEATURE_DATE_COMPAT + int len = strspn(date_str, "0123456789"); + if (date_str[len] == '\0' + || (date_str[len] == '.' + && isdigit(date_str[len+1]) + && isdigit(date_str[len+2]) + && date_str[len+3] == '\0' + ) + ) { + /* Dreaded MMDDhhmm[[CC]YY][.ss] format! + * It does not match -d or -s format. + * Some users actually do use it. + */ + len -= 8; + if (len < 0 || len > 4 || (len & 1)) + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + if (len != 0) { /* move YY or CCYY to front */ + char buf[4]; + memcpy(buf, date_str + 8, len); + memmove(date_str + len, date_str, 8); + memcpy(date_str, buf, len); + } + } +#endif + argv++; + } + } + if (*argv) + bb_show_usage(); + + /* Now we have parsed all the information except the date format + * which depends on whether the clock is being set or read */ + + if (opt & OPT_REFERENCE) { + struct stat statbuf; + xstat(filename, &statbuf); + ts.tv_sec = statbuf.st_mtime; +#if ENABLE_FEATURE_DATE_NANO + ts.tv_nsec = statbuf.st_mtim.tv_nsec; + /* Some toolchains use .st_mtimensec instead of st_mtim.tv_nsec. + * If you need #define _SVID_SOURCE 1 to enable st_mtim.tv_nsec, + * drop a mail to project mailing list please + */ +#endif + } else { +#if ENABLE_FEATURE_DATE_NANO + /* libc has incredibly messy way of doing this, + * typically requiring -lrt. We just skip all this mess */ + syscall(__NR_clock_gettime, CLOCK_REALTIME, &ts); +#else + time(&ts.tv_sec); +#endif + } + localtime_r(&ts.tv_sec, &tm_time); + + /* If date string is given, update tm_time, and maybe set date */ + if (date_str != NULL) { + /* Zero out fields - take her back to midnight! */ + tm_time.tm_sec = 0; + tm_time.tm_min = 0; + tm_time.tm_hour = 0; + + /* Process any date input to UNIX time since 1 Jan 1970 */ + if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_HINT)) { + if (strptime(date_str, fmt_str2dt, &tm_time) == NULL) + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + } else { + parse_datestr(date_str, &tm_time); + } + + /* Correct any day of week and day of year etc. fields */ + /* Be sure to recheck dst (but not if date is time_t format) */ + if (date_str[0] != '@') + tm_time.tm_isdst = -1; + ts.tv_sec = validate_tm_time(date_str, &tm_time); + + /* if setting time, set it */ + if ((opt & OPT_SET) && stime(&ts.tv_sec) < 0) { + bb_perror_msg("can't set date"); + } + } + + /* Display output */ + + /* Deal with format string */ + if (fmt_dt2str == NULL) { + int i; + fmt_dt2str = buf_fmt_dt2str; + if (ENABLE_FEATURE_DATE_ISOFMT && ifmt >= 0) { + /* -I[SPEC]: 0:date 1:hours 2:minutes 3:seconds */ + strcpy(fmt_dt2str, "%Y-%m-%dT%H:%M:%S"); + i = 8 + 3 * ifmt; + if (ifmt != 0) { + /* TODO: if (ifmt==4) i += sprintf(&fmt_dt2str[i], ",%09u", nanoseconds); */ + format_utc: + fmt_dt2str[i++] = '%'; + fmt_dt2str[i++] = (opt & OPT_UTC) ? 'Z' : 'z'; + } + fmt_dt2str[i] = '\0'; + } else if (opt & OPT_RFC2822) { + /* -R. undo busybox.c setlocale */ + if (ENABLE_LOCALE_SUPPORT) + setlocale(LC_TIME, "C"); + strcpy(fmt_dt2str, "%a, %d %b %Y %H:%M:%S "); + i = sizeof("%a, %d %b %Y %H:%M:%S ")-1; + goto format_utc; + } else { /* default case */ + fmt_dt2str = (char*)"%a %b %e %H:%M:%S %Z %Y"; + } + } +#if ENABLE_FEATURE_DATE_NANO + else { + /* User-specified fmt_dt2str */ + /* Search for and process "%N" */ + char *p = fmt_dt2str; + while ((p = strchr(p, '%')) != NULL) { + int n, m; + unsigned pres, scale; + + p++; + if (*p == '%') { + p++; + continue; + } + n = strspn(p, "0123456789"); + if (p[n] != 'N') { + p += n; + continue; + } + /* We have "%[nnn]N" */ + p[-1] = '\0'; + p[n] = '\0'; + scale = 1; + pres = 9; + if (n) { + pres = xatoi_positive(p); + if (pres == 0) + pres = 9; + m = 9 - pres; + while (--m >= 0) + scale *= 10; + } + + m = p - fmt_dt2str; + p += n + 1; + fmt_dt2str = xasprintf("%s%0*u%s", fmt_dt2str, pres, (unsigned)ts.tv_nsec / scale, p); + p = fmt_dt2str + m; + } + } +#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 (is_prefixed_with(fmt_dt2str, "%f")) { + fmt_dt2str = (char*)"%Y.%m.%d-%H:%M:%S"; + } + /* Generate output string */ + strftime(date_buf, COMMON_BUFSIZE, fmt_dt2str, &tm_time); + } + puts(date_buf); + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/dd.c b/busybox/coreutils/dd.c new file mode 100644 index 00000000000000..0f130bcad27f4b --- /dev/null +++ b/busybox/coreutils/dd.c @@ -0,0 +1,584 @@ +/* vi: set sw=4 ts=4: */ +/* + * 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 (7.1 kb)" +//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, iflag and conv options" +//config: default y +//config: depends on DD +//config: help +//config: Enable 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: Enable 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] [iflag=skip_bytes|fullblock]") +//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" +//usage: "\n of=FILE Write to FILE instead of stdout" +//usage: "\n bs=N Read and write N bytes at a time" +//usage: IF_FEATURE_DD_IBS_OBS( +//usage: "\n ibs=N Read N bytes at a time" +//usage: ) +//usage: IF_FEATURE_DD_IBS_OBS( +//usage: "\n obs=N Write N bytes at a time" +//usage: ) +//usage: "\n count=N Copy only N input blocks" +//usage: "\n skip=N Skip N input blocks" +//usage: "\n seek=N Skip N output blocks" +//usage: IF_FEATURE_DD_IBS_OBS( +//usage: "\n conv=notrunc Don't truncate output file" +//usage: "\n conv=noerror Continue after read errors" +//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: "\n iflag=fullblock Read full blocks" +//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), 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" +//usage: "4+0 records in\n" +//usage: "4+0 records out\n" + +#include "libbb.h" +#include "common_bufsiz.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +enum { + ifd = STDIN_FILENO, + ofd = STDOUT_FILENO, +}; + +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 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, + FLAG_FULLBLOCK = (1 << 6) * ENABLE_FEATURE_DD_IBS_OBS, + /* end of input flags */ + FLAG_TWOBUFS = (1 << 7) * ENABLE_FEATURE_DD_IBS_OBS, + FLAG_COUNT = 1 << 8, + FLAG_STATUS_NONE = 1 << 9, + FLAG_STATUS_NOXFER = 1 << 10, +}; + +static void dd_output_status(int UNUSED_PARAM cur_signal) +{ +#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE + double seconds; + unsigned long long bytes_sec; + unsigned long long now_us = monotonic_us(); /* before fprintf */ +#endif + + /* Deliberately using %u, not %d */ + fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n" + "%"OFF_FMT"u+%"OFF_FMT"u records out\n", + G.in_full, G.in_part, + 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 */ + make_human_readable_str(G.total_bytes, 1, 0) + ); + /* Corner cases: + * ./busybox dd /dev/null + * ./busybox dd bs=1M count=2000 /dev/null + * (echo DONE) | ./busybox dd >/dev/null + * (sleep 1; echo DONE) | ./busybox dd >/dev/null + */ + seconds = (now_us - G.begin_time_us) / 1000000.0; + bytes_sec = G.total_bytes / seconds; + fprintf(stderr, "%f seconds, %sB/s\n", + seconds, + /* show fractional digit, use suffixes */ + make_human_readable_str(bytes_sec, 1, 0) + ); +#endif +} + +static ssize_t full_write_or_warn(const void *buf, size_t len, + const char *const filename) +{ + ssize_t n = full_write(ofd, buf, len); + if (n < 0) + bb_perror_msg("writing '%s'", filename); + return n; +} + +static bool write_and_stats(const void *buf, size_t len, size_t obs, + const char *filename) +{ + ssize_t n = full_write_or_warn(buf, len, filename); + if (n < 0) + return 1; +#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE + G.total_bytes += n; +#endif + if ((size_t)n == obs) { + G.out_full++; + return 0; + } + if ((size_t)n == len) { + G.out_part++; + return 0; + } + return 1; +} + +#if ENABLE_LFS +# define XATOU_SFX xatoull_sfx +#else +# 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) +{ + static const char keywords[] ALIGN1 = + "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""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""fullblock\0"; +#endif +#if ENABLE_FEATURE_DD_STATUS + static const char status_words[] ALIGN1 = + "none\0""noxfer\0"; +#endif + enum { + OP_bs = 0, + OP_count, + OP_seek, + 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, + OP_conv_noerror, + OP_conv_fsync, + OP_conv_swab, + /* Unimplemented conv=XXX: */ + //nocreat do not create the output file + //excl fail if the output file already exists + //fdatasync physically write output file data before finishing + //lcase change upper case to lower case + //ucase change lower case to upper case + //block pad newline-terminated records with spaces to cbs-size + //unblock replace trailing spaces in cbs-size records with newline + //ascii from EBCDIC to ASCII + //ebcdic from ASCII to EBCDIC + //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, + OP_iflag_fullblock, +#endif + }; + smallint exitcode = EXIT_FAILURE; + int i; + size_t ibs = 512; + char *ibuf; +#if ENABLE_FEATURE_DD_IBS_OBS + size_t obs = 512; + char *obuf; +#else +# define obs ibs +# define obuf ibuf +#endif + /* These are all zeroed at once! */ + struct { + 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 oc (Z.oc ) +#define prev_read_size (Z.prev_read_size) +#define count (Z.count ) +#define seek (Z.seek ) +#define skip (Z.skip ) +#define infile (Z.infile ) +#define outfile (Z.outfile) + + memset(&Z, 0, sizeof(Z)); + INIT_G(); + //fflush_all(); - is this needed because of NOEXEC? + + for (i = 1; argv[i]; i++) { + int what; + char *val; + char *arg = argv[i]; + +#if ENABLE_DESKTOP + /* "dd --". NB: coreutils 6.9 will complain if they see + * more than one of them. We wouldn't. */ + if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') + continue; +#endif + val = strchr(arg, '='); + if (val == NULL) + bb_show_usage(); + *val = '\0'; + what = index_in_strings(keywords, arg); + if (what < 0) + bb_show_usage(); + /* *val = '='; - to preserve ps listing? */ + val++; +#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, cwbkMG_suffixes); + /*continue;*/ + } + if (what == OP_obs) { + obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes); + /*continue;*/ + } + if (what == OP_conv) { + 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, cwbkMG_suffixes); + obs = ibs; + /*continue;*/ + } + /* These can be large: */ + if (what == OP_count) { + G.flags |= FLAG_COUNT; + count = XATOU_SFX(val, cwbkMG_suffixes); + /*continue;*/ + } + if (what == OP_seek) { + seek = XATOU_SFX(val, cwbkMG_suffixes); + /*continue;*/ + } + if (what == OP_skip) { + skip = XATOU_SFX(val, cwbkMG_suffixes); + /*continue;*/ + } + if (what == OP_if) { + infile = val; + /*continue;*/ + } + if (what == OP_of) { + 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_NONE << n; + /*continue;*/ + } +#endif + } /* end of "for (argv[i])" */ + +//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever + ibuf = xmalloc(ibs); + obuf = ibuf; +#if ENABLE_FEATURE_DD_IBS_OBS + if (ibs != obs) { + G.flags |= FLAG_TWOBUFS; + obuf = xmalloc(obs); + } +#endif + +#if ENABLE_FEATURE_DD_SIGNAL_HANDLING + signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status); +#endif +#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE + G.begin_time_us = monotonic_us(); +#endif + + if (infile) { + xmove_fd(xopen(infile, O_RDONLY), ifd); + } else { + infile = bb_msg_standard_input; + } + if (outfile) { + int oflag = O_WRONLY | O_CREAT; + + if (!seek && !(G.flags & FLAG_NOTRUNC)) + oflag |= O_TRUNC; + + xmove_fd(xopen(outfile, oflag), ofd); + + if (seek && !(G.flags & FLAG_NOTRUNC)) { + if (ftruncate(ofd, seek * obs) < 0) { + struct stat st; + + if (fstat(ofd, &st) < 0 + || S_ISREG(st.st_mode) + || S_ISDIR(st.st_mode) + ) { + goto die_outfile; + } + } + } + } else { + outfile = bb_msg_standard_output; + } + if (skip) { + size_t blocksz = (G.flags & FLAG_SKIP_BYTES) ? 1 : ibs; + if (lseek(ifd, skip * blocksz, SEEK_CUR) < 0) { + do { + ssize_t n; +#if ENABLE_FEATURE_DD_IBS_OBS + if (G.flags & FLAG_FULLBLOCK) + n = full_read(ifd, ibuf, blocksz); + else +#endif + n = safe_read(ifd, ibuf, blocksz); + if (n < 0) + goto die_infile; + if (n == 0) + break; + } while (--skip != 0); + } + } + if (seek) { + if (lseek(ofd, seek * obs, SEEK_CUR) < 0) + goto die_outfile; + } + + while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { + ssize_t n; +#if ENABLE_FEATURE_DD_IBS_OBS + if (G.flags & FLAG_FULLBLOCK) + n = full_read(ifd, ibuf, ibs); + else +#endif + n = safe_read(ifd, ibuf, ibs); + if (n == 0) + break; + if (n < 0) { + /* "Bad block" */ + if (!(G.flags & FLAG_NOERROR)) + goto die_infile; + bb_simple_perror_msg(infile); + /* GNU dd with conv=noerror skips over bad blocks */ + xlseek(ifd, ibs, SEEK_CUR); + /* conv=noerror,sync writes NULs, + * conv=noerror just ignores input bad blocks */ + n = 0; + } + if (G.flags & FLAG_SWAB) { + uint16_t *p16; + ssize_t n2; + + /* Our code allows only last read to be odd-sized */ + if (prev_read_size & 1) + bb_error_msg_and_die("can't swab %lu byte buffer", + (unsigned long)prev_read_size); + prev_read_size = n; + + /* If n is odd, last byte is not swapped: + * echo -n "qwe" | dd conv=swab + * prints "wqe". + */ + p16 = (void*) ibuf; + n2 = (n >> 1); + while (--n2 >= 0) { + *p16 = bswap_16(*p16); + p16++; + } + } + if ((size_t)n == ibs) + G.in_full++; + else { + G.in_part++; + if (G.flags & FLAG_SYNC) { + memset(ibuf + n, 0, ibs - n); + n = ibs; + } + } + if (G.flags & FLAG_TWOBUFS) { + char *tmp = ibuf; + while (n) { + size_t d = obs - oc; + + if (d > (size_t)n) + d = n; + memcpy(obuf + oc, tmp, d); + n -= d; + tmp += d; + oc += d; + if (oc == obs) { + if (write_and_stats(obuf, obs, obs, outfile)) + goto out_status; + oc = 0; + } + } + } else { + if (write_and_stats(ibuf, n, obs, outfile)) + goto out_status; + } + } + + if (G.flags & FLAG_FSYNC) { + if (fsync(ofd) < 0) + goto die_outfile; + } + + if (ENABLE_FEATURE_DD_IBS_OBS && oc) { + if (write_and_stats(obuf, oc, obs, outfile)) + goto out_status; + } + if (close(ifd) < 0) { + die_infile: + bb_simple_perror_msg_and_die(infile); + } + + if (close(ofd) < 0) { + die_outfile: + bb_simple_perror_msg_and_die(outfile); + } + + exitcode = EXIT_SUCCESS; + out_status: + if (!ENABLE_FEATURE_DD_STATUS || !(G.flags & FLAG_STATUS_NONE)) + dd_output_status(0); + + if (ENABLE_FEATURE_CLEAN_UP) { + free(obuf); + if (G.flags & FLAG_TWOBUFS) + free(ibuf); + } + + return exitcode; +} diff --git a/busybox/coreutils/df.c b/busybox/coreutils/df.c new file mode 100644 index 00000000000000..50dccac527e284 --- /dev/null +++ b/busybox/coreutils/df.c @@ -0,0 +1,320 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini df implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * based on original code by (I think) Bruce Perens . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. Removed floating point dependency. Added error checking + * on output. Output stats on 0-sized filesystems if specifically listed on + * the command line. Properly round *-blocks, Used, and Available quantities. + * + * Aug 28, 2008 Bernhard Reutner-Fischer + * + * Implement -P and -B; better coreutils compat; cleanup + */ +//config:config DF +//config: bool "df (7.5 kb)" +//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: -a Show all filesystems +//config: -i Inodes +//config: -B Blocksize + +//applet:IF_DF(APPLET_NOEXEC(df, df, BB_DIR_BIN, BB_SUID_DROP, df)) + +//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" +//usage: "Print filesystem usage statistics\n" +//usage: "\n -P POSIX output format" +//usage: "\n -k 1024-byte blocks (default)" +//usage: IF_FEATURE_HUMAN_READABLE( +//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" +//usage: "\n -B SIZE Blocksize" +//usage: ) +//usage: +//usage:#define df_example_usage +//usage: "$ df\n" +//usage: "Filesystem 1K-blocks Used Available Use% Mounted on\n" +//usage: "/dev/sda3 8690864 8553540 137324 98% /\n" +//usage: "/dev/sda1 64216 36364 27852 57% /boot\n" +//usage: "$ df /dev/sda3\n" +//usage: "Filesystem 1K-blocks Used Available Use% Mounted on\n" +//usage: "/dev/sda3 8690864 8553540 137324 98% /\n" +//usage: "$ POSIXLY_CORRECT=sure df /dev/sda3\n" +//usage: "Filesystem 512B-blocks Used Available Use% Mounted on\n" +//usage: "/dev/sda3 17381728 17107080 274648 98% /\n" +//usage: "$ POSIXLY_CORRECT=yep df -P /dev/sda3\n" +//usage: "Filesystem 512-blocks Used Available Capacity Mounted on\n" +//usage: "/dev/sda3 17381728 17107080 274648 98% /\n" + +#include +#include +#include "libbb.h" +#include "unicode.h" + +#if !ENABLE_FEATURE_HUMAN_READABLE +static unsigned long kscale(unsigned long b, unsigned long bs) +{ + return (b * (unsigned long long) bs + 1024/2) / 1024; +} +#endif + +int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int df_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned long df_disp_hr = 1024; + int status = EXIT_SUCCESS; + unsigned opt; + FILE *mount_table; + struct mntent *mount_entry; + struct statvfs s; + + enum { + OPT_KILO = (1 << 0), + OPT_POSIX = (1 << 1), + 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; + + init_unicode(); + + opt = getopt32(argv, "^" + "kPT" + IF_FEATURE_DF_FANCY("aiB:") + IF_FEATURE_HUMAN_READABLE("hm") + "\0" +#if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY + "k-mB:m-Bk:B-km" +#elif ENABLE_FEATURE_HUMAN_READABLE + "k-m:m-k" +#endif + IF_FEATURE_DF_FANCY(, &chp) + ); + if (opt & OPT_MEGA) + df_disp_hr = 1024*1024; + + 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 + * variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used. + */ + if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */ + df_disp_hr = 512; + + if (opt & OPT_HUMAN) { + df_disp_hr = 0; + disp_units_hdr = " Size"; + } + if (opt & OPT_INODE) + disp_units_hdr = " Inodes"; + + if (disp_units_hdr == NULL) { +#if ENABLE_FEATURE_HUMAN_READABLE + disp_units_hdr = xasprintf("%s-blocks", + /* print df_disp_hr, show no fractionals, + * use suffixes if OPT_POSIX is set in opt */ + make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX)) + ); +#else + disp_units_hdr = xasprintf("%lu-blocks", df_disp_hr); +#endif + } + + 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; + if (!argv[0]) { + mount_table = setmntent(bb_path_mtab_file, "r"); + if (!mount_table) + bb_perror_msg_and_die(bb_path_mtab_file); + } + + while (1) { + const char *device; + const char *mount_point; + const char *fs_type; + + if (mount_table) { + mount_entry = getmntent(mount_table); + if (!mount_entry) { + endmntent(mount_table); + break; + } + } else { + mount_point = *argv++; + if (!mount_point) + break; + mount_entry = find_mount_point(mount_point, 1); + if (!mount_entry) { + bb_error_msg("%s: can't find mount point", mount_point); + set_error: + status = EXIT_FAILURE; + continue; + } + } + + device = mount_entry->mnt_fsname; + + /* GNU coreutils 6.10 skips certain mounts, try to be compatible */ + if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(device, "rootfs") == 0) + continue; + + mount_point = mount_entry->mnt_dir; + fs_type = mount_entry->mnt_type; + + if (statvfs(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)) { + unsigned long long blocks_used; + unsigned long long blocks_total; + unsigned blocks_percent_used; + + if (opt & OPT_INODE) { + s.f_blocks = s.f_files; + s.f_bavail = s.f_bfree = s.f_ffree; + s.f_frsize = 1; + if (df_disp_hr) + df_disp_hr = 1; + } + blocks_used = s.f_blocks - s.f_bfree; + blocks_total = blocks_used + s.f_bavail; + blocks_percent_used = blocks_total; /* 0% if blocks_total == 0, else... */ + if (blocks_total != 0) { + /* Downscale sizes for narrower division */ + unsigned u; + while (blocks_total >= INT_MAX / 101) { + blocks_total >>= 1; + blocks_used >>= 1; + } + u = (unsigned)blocks_used * 100u + (unsigned)blocks_total / 2; + blocks_percent_used = u / (unsigned)blocks_total; + } + +#ifdef WHY_WE_DO_IT_FOR_DEV_ROOT_ONLY + if (strcmp(device, "/dev/root") == 0) { + /* Adjusts device to be the real root device, + * or leaves device alone if it can't find it */ + device = find_block_device("/"); + if (!device) { + goto set_error; + } + } +#endif + +#if ENABLE_UNICODE_SUPPORT + { + uni_stat_t uni_stat; + char *uni_dev = unicode_conv_to_printable(&uni_stat, device); + if (uni_stat.unicode_width > 20 && !(opt & OPT_POSIX)) { + printf("%s\n%20s", uni_dev, ""); + } else { + 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_frsize / df_disp_hr, show one fractional, + * use suffixes if df_disp_hr == 0 */ + make_human_readable_str(s.f_blocks, s.f_frsize, df_disp_hr)); + + printf(" %9s " + 1, + /* 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_frsize, df_disp_hr)); + + printf("%9s %3u%% %s\n", + /* 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_frsize, df_disp_hr), + blocks_percent_used, mount_point); +#else + printf(" %9lu %9lu %9lu %3u%% %s\n", + 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 + } + } + + return status; +} diff --git a/busybox/coreutils/dirname.c b/busybox/coreutils/dirname.c new file mode 100644 index 00000000000000..511267b78ff2ca --- /dev/null +++ b/busybox/coreutils/dirname.c @@ -0,0 +1,43 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dirname implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config DIRNAME +//config: bool "dirname (289 bytes)" +//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 */ + +//usage:#define dirname_trivial_usage +//usage: "FILENAME" +//usage:#define dirname_full_usage "\n\n" +//usage: "Strip non-directory suffix from FILENAME" +//usage: +//usage:#define dirname_example_usage +//usage: "$ dirname /tmp/foo\n" +//usage: "/tmp\n" +//usage: "$ dirname /tmp/foo/\n" +//usage: "/tmp\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int dirname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dirname_main(int argc UNUSED_PARAM, char **argv) +{ + puts(dirname(single_argv(argv))); + return fflush_all(); +} diff --git a/busybox/coreutils/dos2unix.c b/busybox/coreutils/dos2unix.c new file mode 100644 index 00000000000000..9f098e41e714a4 --- /dev/null +++ b/busybox/coreutils/dos2unix.c @@ -0,0 +1,137 @@ +/* vi: set sw=4 ts=4: */ +/* + * dos2unix for BusyBox + * + * dos2unix '\n' converter 0.5.0 + * based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997) + * Copyright 1997,.. by Peter Hanecak . + * All rights reserved. + * + * 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 (5.1 kb)" +//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 (5.1 kb)" +//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]" +//usage:#define dos2unix_full_usage "\n\n" +//usage: "Convert FILE in-place from DOS to Unix format.\n" +//usage: "When no file is given, use stdin/stdout.\n" +//usage: "\n -u dos2unix" +//usage: "\n -d unix2dos" +//usage: +//usage:#define unix2dos_trivial_usage +//usage: "[-ud] [FILE]" +//usage:#define unix2dos_full_usage "\n\n" +//usage: "Convert FILE in-place from Unix to DOS format.\n" +//usage: "When no file is given, use stdin/stdout.\n" +//usage: "\n -u dos2unix" +//usage: "\n -d unix2dos" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +enum { + CT_UNIX2DOS = 1, + CT_DOS2UNIX +}; + +/* if fn is NULL then input is stdin and output is stdout */ +static void convert(char *fn, int conv_type) +{ + FILE *in, *out; + 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); + xfstat(fileno(in), &st, resolved_fn); + + temp_fn = xasprintf("%sXXXXXX", resolved_fn); + 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(fd); + } + + while ((ch = fgetc(in)) != EOF) { + if (ch == '\r') + continue; + if (ch == '\n') + if (conv_type == CT_UNIX2DOS) + fputc('\r', out); + fputc(ch, out); + } + + if (fn != NULL) { + if (fclose(in) < 0 || fclose(out) < 0) { + unlink(temp_fn); + bb_perror_nomsg_and_die(); + } + xrename(temp_fn, resolved_fn); + free(temp_fn); + free(resolved_fn); + } +} + +int dos2unix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dos2unix_main(int argc UNUSED_PARAM, char **argv) +{ + int o, conv_type; + + /* See if we are supposed to be doing dos2unix or unix2dos */ + 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 */ + o = getopt32(argv, "^" "du" "\0" "u--d:d--u"); /* mutually exclusive */ + + /* Do the conversion requested by an argument else do the default + * conversion depending on our name. */ + if (o) + conv_type = o; + + argv += optind; + do { + /* might be convert(NULL) if there is no filename given */ + convert(*argv, conv_type); + } while (*argv && *++argv); + + return 0; +} diff --git a/busybox/coreutils/du.c b/busybox/coreutils/du.c new file mode 100644 index 00000000000000..d5ce46cf2daf29 --- /dev/null +++ b/busybox/coreutils/du.c @@ -0,0 +1,304 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini du implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * Copyright (C) 2002 Edward Betts + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Mostly rewritten for SUSv3 compliance and to fix bugs/defects. + * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options. + * The -d option allows setting of max depth (similar to gnu --max-depth). + * 2) Fixed incorrect size calculations for links and directories, especially + * when errors occurred. Calculates sizes should now match gnu du output. + * 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]..." +//usage:#define du_full_usage "\n\n" +//usage: "Summarize disk space used for each FILE and/or directory\n" +//usage: "\n -a Show file sizes too" +//usage: "\n -L Follow all symlinks" +//usage: "\n -H Follow symlinks on command line" +//usage: "\n -d N Limit output to directories (and files with -a) of depth < N" +//usage: "\n -c Show grand total" +//usage: "\n -l Count sizes many times if hard linked" +//usage: "\n -s Display only a total for each argument" +//usage: "\n -x Skip directories on different filesystems" +//usage: IF_FEATURE_HUMAN_READABLE( +//usage: "\n -h Sizes in human readable format (e.g., 1K 243M 2G)" +//usage: "\n -m Sizes in megabytes" +//usage: ) +//usage: "\n -k Sizes in kilobytes" IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(" (default)") +//usage: IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K( +//usage: "\n Default unit is 512 bytes" +//usage: ) +//usage: +//usage:#define du_example_usage +//usage: "$ du\n" +//usage: "16 ./CVS\n" +//usage: "12 ./kernel-patches/CVS\n" +//usage: "80 ./kernel-patches\n" +//usage: "12 ./tests/CVS\n" +//usage: "36 ./tests\n" +//usage: "12 ./scripts/CVS\n" +//usage: "16 ./scripts\n" +//usage: "12 ./docs/CVS\n" +//usage: "104 ./docs\n" +//usage: "2417 .\n" + +#include "libbb.h" +#include "common_bufsiz.h" + +enum { + OPT_a_files_too = (1 << 0), + OPT_H_follow_links = (1 << 1), + OPT_k_kbytes = (1 << 2), + OPT_L_follow_links = (1 << 3), + OPT_s_total_norecurse = (1 << 4), + OPT_x_one_FS = (1 << 5), + OPT_d_maxdepth = (1 << 6), + OPT_l_hardlinks = (1 << 7), + OPT_c_total = (1 << 8), + OPT_h_for_humans = (1 << 9), + OPT_m_mbytes = (1 << 10), +}; + +struct globals { +#if ENABLE_FEATURE_HUMAN_READABLE + unsigned long disp_unit; +#else + unsigned disp_k; +#endif + int max_print_depth; + bool status; + int slink_depth; + int du_depth; + dev_t dir_dev; +} FIX_ALIASING; +#define G (*(struct globals*)bb_common_bufsiz1) +#define INIT_G() do { setup_common_bufsiz(); } while (0) + + +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_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) { + size++; + size >>= 1; + } + printf("%llu\t%s\n", size, filename); +#endif +} + +/* tiny recursive du */ +static unsigned long long du(const char *filename) +{ + struct stat statbuf; + unsigned long long sum; + + if (lstat(filename, &statbuf) != 0) { + bb_simple_perror_msg(filename); + G.status = EXIT_FAILURE; + return 0; + } + + if (option_mask32 & OPT_x_one_FS) { + if (G.du_depth == 0) { + G.dir_dev = statbuf.st_dev; + } else if (G.dir_dev != statbuf.st_dev) { + return 0; + } + } + + sum = statbuf.st_blocks; + + if (S_ISLNK(statbuf.st_mode)) { + if (G.slink_depth > G.du_depth) { /* -H or -L */ + if (stat(filename, &statbuf) != 0) { + bb_simple_perror_msg(filename); + G.status = EXIT_FAILURE; + return 0; + } + sum = statbuf.st_blocks; + if (G.slink_depth == 1) { + /* Convert -H to -L */ + G.slink_depth = INT_MAX; + } + } + } + + if (!(option_mask32 & OPT_l_hardlinks) + && statbuf.st_nlink > 1 + ) { + /* Add files/directories with links only once */ + if (is_in_ino_dev_hashtable(&statbuf)) { + return 0; + } + add_to_ino_dev_hashtable(&statbuf, NULL); + } + + if (S_ISDIR(statbuf.st_mode)) { + DIR *dir; + struct dirent *entry; + char *newfile; + + dir = warn_opendir(filename); + if (!dir) { + G.status = EXIT_FAILURE; + return sum; + } + + while ((entry = readdir(dir))) { + newfile = concat_subpath_file(filename, entry->d_name); + if (newfile == NULL) + continue; + ++G.du_depth; + sum += du(newfile); + --G.du_depth; + free(newfile); + } + closedir(dir); + } else { + if (!(option_mask32 & OPT_a_files_too) && G.du_depth != 0) + return sum; + } + if (G.du_depth <= G.max_print_depth) { + print(sum, filename); + } + return sum; +} + +int du_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int du_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned long long total; + int slink_depth_save; + unsigned opt; + + INIT_G(); + +#if ENABLE_FEATURE_HUMAN_READABLE + 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_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 */ +#endif + G.max_print_depth = INT_MAX; + + /* Note: SUSv3 specifies that -a and -s options cannot be used together + * in strictly conforming applications. However, it also says that some + * du implementations may produce output when -a and -s are used together. + * gnu du exits with an error code in this case. We choose to simply + * ignore -a. This is consistent with -s being equivalent to -d 0. + */ +#if ENABLE_FEATURE_HUMAN_READABLE + opt = getopt32(argv, "^" + "aHkLsxd:+lchm" + "\0" "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s", + &G.max_print_depth + ); + argv += optind; + if (opt & OPT_h_for_humans) { + G.disp_unit = 0; + } + if (opt & OPT_m_mbytes) { + G.disp_unit = 1024*1024; + } + if (opt & OPT_k_kbytes) { + G.disp_unit = 1024; + } +#else + opt = getopt32(argv, "^" + "aHkLsxd:+lc" + "\0" "H-L:L-H:s-d:d-s", + &G.max_print_depth + ); + argv += optind; +#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K + if (opt & OPT_k_kbytes) { + G.disp_k = 1; + } +#endif +#endif + if (opt & OPT_H_follow_links) { + G.slink_depth = 1; + } + if (opt & OPT_L_follow_links) { + G.slink_depth = INT_MAX; + } + if (opt & OPT_s_total_norecurse) { + G.max_print_depth = 0; + } + + /* go through remaining args (if any) */ + if (!*argv) { + *--argv = (char*)"."; + if (G.slink_depth == 1) { + G.slink_depth = 0; + } + } + + slink_depth_save = G.slink_depth; + total = 0; + do { + total += du(*argv); + /* otherwise du /dir /dir won't show /dir twice: */ + reset_ino_dev_hashtable(); + G.slink_depth = slink_depth_save; + } while (*++argv); + + if (opt & OPT_c_total) + print(total, "total"); + + fflush_stdout_and_exit(G.status); +} diff --git a/busybox/coreutils/echo.c b/busybox/coreutils/echo.c new file mode 100644 index 00000000000000..e45b909402a6cd --- /dev/null +++ b/busybox/coreutils/echo.c @@ -0,0 +1,349 @@ +/* vi: set sw=4 ts=4: */ +/* + * echo implementation for busybox + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Original copyright notice is retained at the end of this file. + */ +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Because of behavioral differences, implemented configurable SUSv3 + * or 'fancy' gnu-ish behaviors. Also, reduced size and fixed bugs. + * 1) In handling '\c' escape, the previous version only suppressed the + * trailing newline. SUSv3 specifies _no_ output after '\c'. + * 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 -n and -e options" +//config: default y +//config: depends on ECHO || ASH_ECHO || HUSH_ECHO + +//applet:IF_ECHO(APPLET_NOFORK(echo, echo, BB_DIR_BIN, BB_SUID_DROP, echo)) + +//kbuild:lib-$(CONFIG_ECHO) += echo.o + +//kbuild:lib-$(CONFIG_ASH_ECHO) += echo.o +//kbuild:lib-$(CONFIG_HUSH_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]..." +//usage:#define echo_full_usage "\n\n" +//usage: "Print the specified ARGs to stdout" +//usage: IF_FEATURE_FANCY_ECHO( "\n" +//usage: "\n -n Suppress trailing newline" +//usage: "\n -e Interpret backslash escapes (i.e., \\t=tab)" +//usage: "\n -E Don't interpret backslash escapes (default)" +//usage: ) +//usage: +//usage:#define echo_example_usage +//usage: "$ echo \"Erik is cool\"\n" +//usage: "Erik is cool\n" +//usage: IF_FEATURE_FANCY_ECHO("$ echo -e \"Erik\\nis\\ncool\"\n" +//usage: "Erik\n" +//usage: "is\n" +//usage: "cool\n" +//usage: "$ echo \"Erik\\nis\\ncool\"\n" +//usage: "Erik\\nis\\ncool\n") + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +/* NB: can be used by shell even if not enabled as applet */ + +/* + * NB2: we don't use stdio, we need better error handing. + * Examples include writing into non-opened stdout and error on write. + * + * With stdio, output gets shoveled into stdout buffer, and even + * fflush cannot clear it out. It seems that even if libc receives + * EBADF on write attempts, it feels determined to output data no matter what. + * If echo is called by shell, it will try writing again later, and possibly + * will clobber future output. Not good. + * + * Solaris has fpurge which discards buffered input. glibc has __fpurge. + * But this function is not standard. + */ + +int echo_main(int argc UNUSED_PARAM, char **argv) +{ + char **pp; + const char *arg; + char *out; + char *buffer; + unsigned buflen; +#if !ENABLE_FEATURE_FANCY_ECHO + enum { + eflag = 0, /* 0 -- disable escape sequences */ + nflag = 1, /* 1 -- print '\n' */ + }; + + argv++; +#else + char nflag = 1; + char eflag = 0; + + while ((arg = *++argv) != NULL) { + char n, e; + + if (arg[0] != '-') + break; /* not an option arg, echo it */ + + /* If it appears that we are handling options, then make sure + * that all of the options specified are actually valid. + * Otherwise, the string should just be echoed. + */ + arg++; + n = nflag; + e = eflag; + do { + if (*arg == 'n') + n = 0; + else if (*arg == 'e') + e = '\\'; + else if (*arg != 'E') { + /* "-ccc" arg with one of c's invalid, echo it */ + /* arg consisting from just "-" also handled here */ + goto just_echo; + } + } while (*++arg); + nflag = n; + eflag = e; + } + just_echo: +#endif + + buflen = 0; + pp = argv; + while ((arg = *pp) != NULL) { + buflen += strlen(arg) + 1; + pp++; + } + out = buffer = xmalloc(buflen + 1); /* +1 is needed for "no args" case */ + + while ((arg = *argv) != NULL) { + int c; + + if (!eflag) { + /* optimization for very common case */ + out = stpcpy(out, arg); + } else + while ((c = *arg++) != '\0') { + if (c == eflag) { + /* This is an "\x" sequence */ + + if (*arg == 'c') { + /* "\c" means cancel newline and + * ignore all subsequent chars. */ + goto do_write; + } + /* Since SUSv3 mandates a first digit of 0, 4-digit octals + * of the form \0### are accepted. */ + if (*arg == '0') { + if ((unsigned char)(arg[1] - '0') < 8) { + /* 2nd char is 0..7: skip leading '0' */ + arg++; + } + } + /* bb_process_escape_sequence handles NUL correctly + * ("...\" case). */ + { + /* optimization: don't force arg to be on-stack, + * use another variable for that. ~30 bytes win */ + const char *z = arg; + c = bb_process_escape_sequence(&z); + arg = z; + } + } + *out++ = c; + } + + if (!*++argv) + break; + *out++ = ' '; + } + + if (nflag) { + *out++ = '\n'; + } + + do_write: + /* Careful to error out on partial writes too (think ENOSPC!) */ + errno = 0; + /*r =*/ full_write(STDOUT_FILENO, buffer, out - buffer); + free(buffer); + if (/*WRONG:r < 0*/ errno) { + bb_perror_msg(bb_msg_write_error); + return 1; + } + return 0; +} + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. + * + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)echo.c 8.1 (Berkeley) 5/31/93 + */ + +#ifdef VERSION_WITH_WRITEV +/* We can't use stdio. + * The reason for this is highly non-obvious. + * echo_main is used from shell. Shell must correctly handle "echo foo" + * if stdout is closed. With stdio, output gets shoveled into + * stdout buffer, and even fflush cannot clear it out. It seems that + * even if libc receives EBADF on write attempts, it feels determined + * to output data no matter what. So it will try later, + * and possibly will clobber future output. Not good. + * + * Using writev instead, with 'direct' conversion of argv vector. + */ + +int echo_main(int argc, char **argv) +{ + struct iovec io[argc]; + struct iovec *cur_io = io; + char *arg; + char *p; +#if !ENABLE_FEATURE_FANCY_ECHO + enum { + eflag = '\\', + nflag = 1, /* 1 -- print '\n' */ + }; + arg = *++argv; + if (!arg) + goto newline_ret; +#else + char nflag = 1; + char eflag = 0; + + while (1) { + arg = *++argv; + if (!arg) + goto newline_ret; + if (*arg != '-') + break; + + /* If it appears that we are handling options, then make sure + * that all of the options specified are actually valid. + * Otherwise, the string should just be echoed. + */ + p = arg + 1; + if (!*p) /* A single '-', so echo it. */ + goto just_echo; + + do { + if (!strchr("neE", *p)) + goto just_echo; + } while (*++p); + + /* All of the options in this arg are valid, so handle them. */ + p = arg + 1; + do { + if (*p == 'n') + nflag = 0; + if (*p == 'e') + eflag = '\\'; + } while (*++p); + } + just_echo: +#endif + + while (1) { + /* arg is already == *argv and isn't NULL */ + int c; + + cur_io->iov_base = p = arg; + + if (!eflag) { + /* optimization for very common case */ + p += strlen(arg); + } else while ((c = *arg++)) { + if (c == eflag) { + /* This is an "\x" sequence */ + + if (*arg == 'c') { + /* "\c" means cancel newline and + * ignore all subsequent chars. */ + cur_io->iov_len = p - (char*)cur_io->iov_base; + cur_io++; + goto ret; + } + /* Since SUSv3 mandates a first digit of 0, 4-digit octals + * of the form \0### are accepted. */ + if (*arg == '0' && (unsigned char)(arg[1] - '0') < 8) { + arg++; + } + /* bb_process_escape_sequence can handle nul correctly */ + c = bb_process_escape_sequence( (void*) &arg); + } + *p++ = c; + } + + arg = *++argv; + if (arg) + *p++ = ' '; + cur_io->iov_len = p - (char*)cur_io->iov_base; + cur_io++; + if (!arg) + break; + } + + newline_ret: + if (nflag) { + cur_io->iov_base = (char*)"\n"; + cur_io->iov_len = 1; + cur_io++; + } + ret: + /* TODO: implement and use full_writev? */ + return writev(1, io, (cur_io - io)) >= 0; +} +#endif diff --git a/busybox/coreutils/env.c b/busybox/coreutils/env.c new file mode 100644 index 00000000000000..0aebead1b07b3f --- /dev/null +++ b/busybox/coreutils/env.c @@ -0,0 +1,132 @@ +/* vi: set sw=4 ts=4: */ +/* + * env implementation for busybox + * + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Original copyright notice is retained at the end of this file. + * + * Modified for BusyBox by Erik Andersen + */ +/* 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 (C) 2003 + * - correct "-" option usage + * - multiple "-u unsetenv" support + * - GNU long option support + * - use xfunc_error_retval + */ +//config:config ENV +//config: bool "env (3.8 kb)" +//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. + +//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]" +//usage:#define env_full_usage "\n\n" +//usage: "Print the current environment or run PROG after setting up\n" +//usage: "the specified environment\n" +//usage: "\n -, -i Start with an empty environment" +//usage: "\n -u Remove variable from the environment" + +#include "libbb.h" + +int env_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int env_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opts; + llist_t *unset_env = NULL; + + opts = getopt32long(argv, "+iu:*", + "ignore-environment\0" No_argument "i" + "unset\0" Required_argument "u" + , &unset_env + ); + argv += optind; + if (argv[0] && LONE_DASH(argv[0])) { + opts |= 1; + ++argv; + } + if (opts & 1) { + clearenv(); + } + while (unset_env) { + char *var = llist_pop(&unset_env); + /* This does not handle -uVAR=VAL + * (coreutils _sets_ the variable in that case): */ + /*unsetenv(var);*/ + /* This does, but uses somewhan undocumented feature that + * putenv("name_without_equal_sign") unsets the variable: */ + putenv(var); + } + + while (*argv && (strchr(*argv, '=') != NULL)) { + if (putenv(*argv) < 0) { + bb_perror_msg_and_die("putenv"); + } + ++argv; + } + + if (argv[0]) { + BB_EXECVP_or_die(argv); + } + + if (environ) { /* clearenv() may set environ == NULL! */ + char **ep; + for (ep = environ; *ep; ep++) { + puts(*ep); + } + } + + fflush_stdout_and_exit(EXIT_SUCCESS); +} + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. BSD Advertising Clause omitted per the July 22, 1999 licensing change + * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change + * + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/busybox/coreutils/expand.c b/busybox/coreutils/expand.c new file mode 100644 index 00000000000000..91084b80b1638d --- /dev/null +++ b/busybox/coreutils/expand.c @@ -0,0 +1,224 @@ +/* expand - convert tabs to spaces + * unexpand - convert spaces to tabs + * + * Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * David MacKenzie + * + * Options for expand: + * -t num --tabs NUM Convert tabs to num spaces (default 8 spaces). + * -i --initial Only convert initial tabs on each line to spaces. + * + * Options for unexpand: + * -a --all Convert all blanks, instead of just initial blanks. + * -f --first-only Convert only leading sequences of blanks (default). + * -t num --tabs NUM Have tabs num characters apart instead of 8. + * + * Busybox version (C) 2007 by Tito Ragusa + * + * Caveat: this versions of expand and unexpand don't accept tab lists. + */ +//config:config EXPAND +//config: bool "expand (5.8 kb)" +//config: default y +//config: help +//config: By default, convert all tabs to spaces. +//config: +//config:config UNEXPAND +//config: bool "unexpand (6 kb)" +//config: default y +//config: help +//config: By default, convert only leading sequences of blanks to tabs. + +//applet:IF_EXPAND(APPLET(expand, BB_DIR_USR_BIN, BB_SUID_DROP)) +// APPLET_ODDNAME:name main location suid_type help +//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]..." +//usage:#define expand_full_usage "\n\n" +//usage: "Convert tabs to spaces, writing to stdout\n" +//usage: "\n -i Don't convert tabs after non blanks" +//usage: "\n -t Tabstops every N chars" + +//usage:#define unexpand_trivial_usage +//usage: "[-fa][-t N] [FILE]..." +//usage:#define unexpand_full_usage "\n\n" +//usage: "Convert spaces to tabs, writing to stdout\n" +//usage: "\n -a Convert all blanks" +//usage: "\n -f Convert only leading blanks" +//usage: "\n -t N Tabstops every N chars" + +#include "libbb.h" +#include "unicode.h" + +enum { + OPT_INITIAL = 1 << 0, + OPT_TABS = 1 << 1, + OPT_ALL = 1 << 2, +}; + +#if ENABLE_EXPAND +static void expand(FILE *file, unsigned tab_size, unsigned opt) +{ + char *line; + + while ((line = xmalloc_fgets(file)) != NULL) { + unsigned char c; + char *ptr; + char *ptr_strbeg; + + ptr = ptr_strbeg = line; + while ((c = *ptr) != '\0') { + if ((opt & OPT_INITIAL) && !isblank(c)) { + /* not space or tab */ + break; + } + if (c == '\t') { + unsigned len; + *ptr = '\0'; +# if ENABLE_UNICODE_SUPPORT + len = unicode_strwidth(ptr_strbeg); +# else + len = ptr - ptr_strbeg; +# endif + len = tab_size - (len % tab_size); + /*while (ptr[1] == '\t') { ptr++; len += tab_size; } - can handle many tabs at once */ + printf("%s%*s", ptr_strbeg, len, ""); + ptr_strbeg = ptr + 1; + } + ptr++; + } + fputs(ptr_strbeg, stdout); + free(line); + } +} +#endif + +#if ENABLE_UNEXPAND +static void unexpand(FILE *file, unsigned tab_size, unsigned opt) +{ + char *line; + + while ((line = xmalloc_fgets(file)) != NULL) { + char *ptr = line; + unsigned column = 0; + + while (*ptr) { + unsigned n; + unsigned len = 0; + + while (*ptr == ' ') { + ptr++; + len++; + } + column += len; + if (*ptr == '\t') { + column += tab_size - (column % tab_size); + ptr++; + continue; + } + + n = column / tab_size; + if (n) { + len = column = column % tab_size; + while (n--) + putchar('\t'); + } + + if ((opt & OPT_INITIAL) && ptr != line) { + printf("%*s%s", len, "", ptr); + break; + } + n = strcspn(ptr, "\t "); + printf("%*s%.*s", len, "", n, ptr); +# if ENABLE_UNICODE_SUPPORT + { + char c = ptr[n]; + ptr[n] = '\0'; + len = unicode_strwidth(ptr); + ptr[n] = c; + } +# else + len = n; +# endif + ptr += n; + column = (column + len) % tab_size; + } + free(line); + } +} +#endif + +int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int expand_main(int argc UNUSED_PARAM, char **argv) +{ + /* Default 8 spaces for 1 tab */ + const char *opt_t = "8"; + FILE *file; + unsigned tab_size; + unsigned opt; + int exit_status = EXIT_SUCCESS; + + init_unicode(); + + if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) { + opt = getopt32long(argv, "it:", + "initial\0" No_argument "i" + "tabs\0" Required_argument "t" + , &opt_t + ); + } else { + opt = getopt32long(argv, "^" + "ft:a" + "\0" + "ta" /* -t NUM sets -a */, + "first-only\0" No_argument "i" + "tabs\0" Required_argument "t" + "all\0" No_argument "a" + , &opt_t + ); + /* -f --first-only is the default */ + if (!(opt & OPT_ALL)) opt |= OPT_INITIAL; + } + tab_size = xatou_range(opt_t, 1, UINT_MAX); + + argv += optind; + + if (!*argv) { + *--argv = (char*)bb_msg_standard_input; + } + do { + file = fopen_or_warn_stdin(*argv); + if (!file) { + exit_status = EXIT_FAILURE; + continue; + } + + if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) + IF_EXPAND(expand(file, tab_size, opt)); + else + IF_UNEXPAND(unexpand(file, tab_size, opt)); + + /* Check and close the file */ + if (fclose_if_not_stdin(file)) { + bb_simple_perror_msg(*argv); + exit_status = EXIT_FAILURE; + } + /* If stdin also clear EOF */ + if (file == stdin) + clearerr(file); + } while (*++argv); + + /* Now close stdin also */ + /* (if we didn't read from it, it's a no-op) */ + if (fclose(stdin)) + bb_perror_msg_and_die(bb_msg_standard_input); + + fflush_stdout_and_exit(exit_status); +} diff --git a/busybox/coreutils/expr.c b/busybox/coreutils/expr.c new file mode 100644 index 00000000000000..e54afbb62e3ff5 --- /dev/null +++ b/busybox/coreutils/expr.c @@ -0,0 +1,559 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini expr implementation for busybox + * + * based on GNU expr Mike Parker. + * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc. + * + * Busybox modifications + * Copyright (c) 2000 Edward Betts . + * Copyright (C) 2003-2005 Vladimir Oleynik + * - reduced 464 bytes. + * - 64 math support + * + * 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 + * it is language-specific. It is especially nice for expressions. + * + * 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). + */ +//config:config EXPR +//config: bool "expr (6.1 kb)" +//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_NOEXEC(expr, expr, BB_DIR_USR_BIN, BB_SUID_DROP, expr)) + +//kbuild:lib-$(CONFIG_EXPR) += expr.o + +//usage:#define expr_trivial_usage +//usage: "EXPRESSION" +//usage:#define expr_full_usage "\n\n" +//usage: "Print the value of EXPRESSION to stdout\n" +//usage: "\n" +//usage: "EXPRESSION may be:\n" +//usage: " ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" +//usage: " ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" +//usage: " ARG1 < ARG2 1 if ARG1 is less than ARG2, else 0. Similarly:\n" +//usage: " ARG1 <= ARG2\n" +//usage: " ARG1 = ARG2\n" +//usage: " ARG1 != ARG2\n" +//usage: " ARG1 >= ARG2\n" +//usage: " ARG1 > ARG2\n" +//usage: " ARG1 + ARG2 Sum of ARG1 and ARG2. Similarly:\n" +//usage: " ARG1 - ARG2\n" +//usage: " ARG1 * ARG2\n" +//usage: " ARG1 / ARG2\n" +//usage: " ARG1 % ARG2\n" +//usage: " STRING : REGEXP Anchored pattern match of REGEXP in STRING\n" +//usage: " match STRING REGEXP Same as STRING : REGEXP\n" +//usage: " substr STRING POS LENGTH Substring of STRING, POS counted from 1\n" +//usage: " index STRING CHARS Index in STRING where any CHARS is found, or 0\n" +//usage: " length STRING Length of STRING\n" +//usage: " quote TOKEN Interpret TOKEN as a string, even if\n" +//usage: " it is a keyword like 'match' or an\n" +//usage: " operator like '/'\n" +//usage: " (EXPRESSION) Value of EXPRESSION\n" +//usage: "\n" +//usage: "Beware that many operators need to be escaped or quoted for shells.\n" +//usage: "Comparisons are arithmetic if both ARGs are numbers, else\n" +//usage: "lexicographical. Pattern matches return the string matched between\n" +//usage: "\\( and \\) or null; if \\( and \\) are not used, they return the number\n" +//usage: "of characters matched or 0." + +#include "libbb.h" +#include "common_bufsiz.h" +#include "xregex.h" + +#if ENABLE_EXPR_MATH_SUPPORT_64 +typedef int64_t arith_t; + +#define PF_REZ "ll" +#define PF_REZ_TYPE (long long) +#define STRTOL(s, e, b) strtoll(s, e, b) +#else +typedef long arith_t; + +#define PF_REZ "l" +#define PF_REZ_TYPE (long) +#define STRTOL(s, e, b) strtol(s, e, b) +#endif + +/* TODO: use bb_strtol[l]? It's easier to check for errors... */ + +/* The kinds of value we can have. */ +enum { + INTEGER, + STRING +}; + +/* A value is.... */ +struct valinfo { + smallint type; /* Which kind. */ + union { /* The value itself. */ + arith_t i; + char *s; + } u; +}; +typedef struct valinfo VALUE; + +/* The arguments given to the program, minus the program name. */ +struct globals { + char **args; +} FIX_ALIASING; +#define G (*(struct globals*)bb_common_bufsiz1) +#define INIT_G() do { \ + setup_common_bufsiz(); \ + /* NB: noexec applet - globals not zeroed */ \ +} while (0) + +/* forward declarations */ +static VALUE *eval(void); + + +/* Return a VALUE for I. */ + +static VALUE *int_value(arith_t i) +{ + VALUE *v; + + v = xzalloc(sizeof(VALUE)); + if (INTEGER) /* otherwise xzalloc did it already */ + v->type = INTEGER; + v->u.i = i; + return v; +} + +/* Return a VALUE for S. */ + +static VALUE *str_value(const char *s) +{ + VALUE *v; + + v = xzalloc(sizeof(VALUE)); + if (STRING) /* otherwise xzalloc did it already */ + v->type = STRING; + v->u.s = xstrdup(s); + return v; +} + +/* Free VALUE V, including structure components. */ + +static void freev(VALUE *v) +{ + if (v->type == STRING) + free(v->u.s); + free(v); +} + +/* Return nonzero if V is a null-string or zero-number. */ + +static int null(VALUE *v) +{ + if (v->type == INTEGER) + return v->u.i == 0; + /* STRING: */ + return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0'); +} + +/* Coerce V to a STRING value (can't fail). */ + +static void tostring(VALUE *v) +{ + if (v->type == INTEGER) { + v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i); + v->type = STRING; + } +} + +/* Coerce V to an INTEGER value. Return 1 on success, 0 on failure. */ + +static bool toarith(VALUE *v) +{ + if (v->type == STRING) { + arith_t i; + char *e; + + /* Don't interpret the empty string as an integer. */ + /* Currently does not worry about overflow or int/long differences. */ + i = STRTOL(v->u.s, &e, 10); + if ((v->u.s == e) || *e) + return 0; + free(v->u.s); + v->u.i = i; + v->type = INTEGER; + } + return 1; +} + +/* Return str[0]+str[1] if the next token matches STR exactly. + STR must not be NULL. */ + +static int nextarg(const char *str) +{ + if (*G.args == NULL || strcmp(*G.args, str) != 0) + return 0; + return (unsigned char)str[0] + (unsigned char)str[1]; +} + +/* The comparison operator handling functions. */ + +static int cmp_common(VALUE *l, VALUE *r, int op) +{ + arith_t ll, rr; + + ll = l->u.i; + rr = r->u.i; + if (l->type == STRING || r->type == STRING) { + tostring(l); + tostring(r); + ll = strcmp(l->u.s, r->u.s); + rr = 0; + } + /* calculating ll - rr and checking the result is prone to overflows. + * We'll do it differently: */ + if (op == '<') + return ll < rr; + if (op == ('<' + '=')) + return ll <= rr; + if (op == '=' || (op == '=' + '=')) + return ll == rr; + if (op == '!' + '=') + return ll != rr; + if (op == '>') + return ll > rr; + /* >= */ + return ll >= rr; +} + +/* The arithmetic operator handling functions. */ + +static arith_t arithmetic_common(VALUE *l, VALUE *r, int op) +{ + arith_t li, ri; + + if (!toarith(l) || !toarith(r)) + bb_error_msg_and_die("non-numeric argument"); + li = l->u.i; + ri = r->u.i; + if (op == '+') + return li + ri; + if (op == '-') + return li - ri; + if (op == '*') + return li * ri; + if (ri == 0) + bb_error_msg_and_die("division by zero"); + if (op == '/') + return li / ri; + return li % ri; +} + +/* Do the : operator. + SV is the VALUE for the lhs (the string), + PV is the VALUE for the rhs (the pattern). */ + +static VALUE *docolon(VALUE *sv, VALUE *pv) +{ + enum { NMATCH = 2 }; + VALUE *v; + regex_t re_buffer; + regmatch_t re_regs[NMATCH]; + + tostring(sv); + tostring(pv); + + if (pv->u.s[0] == '^') { + bb_error_msg( +"warning: '%s': using '^' as the first character\n" +"of a basic regular expression is not portable; it is ignored", pv->u.s); + } + + memset(&re_buffer, 0, sizeof(re_buffer)); + memset(re_regs, 0, sizeof(re_regs)); + xregcomp(&re_buffer, pv->u.s, 0); + + /* expr uses an anchored pattern match, so check that there was a + * match and that the match starts at offset 0. */ + if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH + && re_regs[0].rm_so == 0 + ) { + /* Were \(...\) used? */ + if (re_buffer.re_nsub > 0 && re_regs[1].rm_so >= 0) { + sv->u.s[re_regs[1].rm_eo] = '\0'; + v = str_value(sv->u.s + re_regs[1].rm_so); + } else { + v = int_value(re_regs[0].rm_eo); + } + } else { + /* Match failed -- return the right kind of null. */ + if (re_buffer.re_nsub > 0) + v = str_value(""); + else + v = int_value(0); + } + regfree(&re_buffer); + return v; +} + +/* Handle bare operands and ( expr ) syntax. */ + +static VALUE *eval7(void) +{ + VALUE *v; + + if (!*G.args) + bb_error_msg_and_die("syntax error"); + + if (nextarg("(")) { + G.args++; + v = eval(); + if (!nextarg(")")) + bb_error_msg_and_die("syntax error"); + G.args++; + return v; + } + + if (nextarg(")")) + bb_error_msg_and_die("syntax error"); + + return str_value(*G.args++); +} + +/* Handle match, substr, index, length, and quote keywords. */ + +static VALUE *eval6(void) +{ + static const char keywords[] ALIGN1 = + "quote\0""length\0""match\0""index\0""substr\0"; + + VALUE *r, *i1, *i2; + VALUE *l = l; /* silence gcc */ + VALUE *v = v; /* silence gcc */ + int key = *G.args ? index_in_strings(keywords, *G.args) + 1 : 0; + + if (key == 0) /* not a keyword */ + return eval7(); + G.args++; /* We have a valid token, so get the next argument. */ + if (key == 1) { /* quote */ + if (!*G.args) + bb_error_msg_and_die("syntax error"); + return str_value(*G.args++); + } + if (key == 2) { /* length */ + r = eval6(); + tostring(r); + v = int_value(strlen(r->u.s)); + freev(r); + } else + l = eval6(); + + if (key == 3) { /* match */ + r = eval6(); + v = docolon(l, r); + freev(l); + freev(r); + } + if (key == 4) { /* index */ + r = eval6(); + tostring(l); + tostring(r); + v = int_value(strcspn(l->u.s, r->u.s) + 1); + if (v->u.i == (arith_t) strlen(l->u.s) + 1) + v->u.i = 0; + freev(l); + freev(r); + } + if (key == 5) { /* substr */ + i1 = eval6(); + i2 = eval6(); + tostring(l); + if (!toarith(i1) || !toarith(i2) + || i1->u.i > (arith_t) strlen(l->u.s) + || i1->u.i <= 0 || i2->u.i <= 0) + v = str_value(""); + else { + v = xmalloc(sizeof(VALUE)); + v->type = STRING; + v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i); + } + freev(l); + freev(i1); + freev(i2); + } + return v; +} + +/* Handle : operator (pattern matching). + Calls docolon to do the real work. */ + +static VALUE *eval5(void) +{ + VALUE *l, *r, *v; + + l = eval6(); + while (nextarg(":")) { + G.args++; + r = eval6(); + v = docolon(l, r); + freev(l); + freev(r); + l = v; + } + return l; +} + +/* Handle *, /, % operators. */ + +static VALUE *eval4(void) +{ + VALUE *l, *r; + int op; + arith_t val; + + l = eval5(); + while (1) { + op = nextarg("*"); + if (!op) { op = nextarg("/"); + if (!op) { op = nextarg("%"); + if (!op) return l; + }} + G.args++; + r = eval5(); + val = arithmetic_common(l, r, op); + freev(l); + freev(r); + l = int_value(val); + } +} + +/* Handle +, - operators. */ + +static VALUE *eval3(void) +{ + VALUE *l, *r; + int op; + arith_t val; + + l = eval4(); + while (1) { + op = nextarg("+"); + if (!op) { + op = nextarg("-"); + if (!op) return l; + } + G.args++; + r = eval4(); + val = arithmetic_common(l, r, op); + freev(l); + freev(r); + l = int_value(val); + } +} + +/* Handle comparisons. */ + +static VALUE *eval2(void) +{ + VALUE *l, *r; + int op; + arith_t val; + + l = eval3(); + while (1) { + op = nextarg("<"); + if (!op) { op = nextarg("<="); + if (!op) { op = nextarg("="); + if (!op) { op = nextarg("=="); + if (!op) { op = nextarg("!="); + if (!op) { op = nextarg(">="); + if (!op) { op = nextarg(">"); + if (!op) return l; + }}}}}} + G.args++; + r = eval3(); + toarith(l); + toarith(r); + val = cmp_common(l, r, op); + freev(l); + freev(r); + l = int_value(val); + } +} + +/* Handle &. */ + +static VALUE *eval1(void) +{ + VALUE *l, *r; + + l = eval2(); + while (nextarg("&")) { + G.args++; + r = eval2(); + if (null(l) || null(r)) { + freev(l); + freev(r); + l = int_value(0); + } else + freev(r); + } + return l; +} + +/* Handle |. */ + +static VALUE *eval(void) +{ + VALUE *l, *r; + + l = eval1(); + while (nextarg("|")) { + G.args++; + r = eval1(); + if (null(l)) { + freev(l); + l = r; + } else + freev(r); + } + return l; +} + +int expr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int expr_main(int argc UNUSED_PARAM, char **argv) +{ + VALUE *v; + + INIT_G(); + + xfunc_error_retval = 2; /* coreutils compat */ + G.args = argv + 1; + if (*G.args == NULL) { + bb_error_msg_and_die("too few arguments"); + } + v = eval(); + if (*G.args) + bb_error_msg_and_die("syntax error"); + if (v->type == INTEGER) + printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i); + else + puts(v->u.s); + fflush_stdout_and_exit(null(v)); +} diff --git a/busybox/coreutils/factor.c b/busybox/coreutils/factor.c new file mode 100644 index 00000000000000..467e23a4d81aeb --- /dev/null +++ b/busybox/coreutils/factor.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2017 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config FACTOR +//config: bool "factor (2.6 kb)" +//config: default y +//config: help +//config: factor factorizes integers + +//applet:IF_FACTOR(APPLET(factor, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_FACTOR) += factor.o + +//usage:#define factor_trivial_usage +//usage: "[NUMBER]..." +//usage:#define factor_full_usage "\n\n" +//usage: "Print prime factors" + +#include "libbb.h" + +#if 0 +# define dbg(...) bb_error_msg(__VA_ARGS__) +#else +# define dbg(...) ((void)0) +#endif + +typedef unsigned long long wide_t; + +#if ULLONG_MAX == (UINT_MAX * UINT_MAX + 2 * UINT_MAX) +/* "unsigned" is half as wide as ullong */ +typedef unsigned half_t; +#define HALF_MAX UINT_MAX +#define HALF_FMT "" +#elif ULLONG_MAX == (ULONG_MAX * ULONG_MAX + 2 * ULONG_MAX) +/* long is half as wide as ullong */ +typedef unsigned long half_t; +#define HALF_MAX ULONG_MAX +#define HALF_FMT "l" +#else +#error Cant find an integer type which is half as wide as ullong +#endif + +static half_t isqrt_odd(wide_t N) +{ + half_t s = isqrt(N); + /* Subtract 1 from even s, odd s won't change: */ + /* (doesnt work for zero, but we know that s != 0 here) */ + s = (s - 1) | 1; + return s; +} + +static NOINLINE void factorize(wide_t N) +{ + half_t factor; + half_t max_factor; + // unsigned count3; + // unsigned count5; + // unsigned count7; + // ^^^^^^^^^^^^^^^ commented-out simple sieving code (easier to grasp). + // Faster sieving, using one word for potentially up to 6 counters: + // count upwards in each mask, counter "triggers" when it sets its mask to "100[0]..." + // 10987654321098765432109876543210 - bits 31-0 in 32-bit word + // 17777713333311111777775555333 - bit masks for counters for primes 3,5,7,11,13,17 + // 100000100001000010001001 - value for adding 1 to each mask + // 10000010000010000100001000100 - value for checking that any mask reached msb + enum { + SHIFT_3 = 1 << 0, + SHIFT_5 = 1 << 3, + SHIFT_7 = 1 << 7, + INCREMENT_EACH = SHIFT_3 | SHIFT_5 | SHIFT_7, + MULTIPLE_OF_3 = 1 << 2, + MULTIPLE_OF_5 = 1 << 6, + MULTIPLE_OF_7 = 1 << 11, + MULTIPLE_DETECTED = MULTIPLE_OF_3 | MULTIPLE_OF_5 | MULTIPLE_OF_7, + }; + unsigned sieve_word; + + if (N < 4) + goto end; + + while (!(N & 1)) { + printf(" 2"); + N >>= 1; + } + + /* The code needs to be optimized for the case where + * there are large prime factors. For example, + * this is not hard: + * 8262075252869367027 = 3 7 17 23 47 101 113 127 131 137 823 + * (the largest factor to test is only ~sqrt(823) = 28) + * but this is: + * 18446744073709551601 = 53 348051774975651917 + * the last factor requires testing up to + * 589959129 - about 100 million iterations. + * The slowest case (largest prime) for N < 2^64 is + * factor 18446744073709551557 (0xffffffffffffffc5). + */ + max_factor = isqrt_odd(N); + // count3 = 3; + // count5 = 6; + // count7 = 9; + sieve_word = 0 + /* initial count for SHIFT_n is (n-1)/2*3: */ + + (MULTIPLE_OF_3 - 3 * SHIFT_3) + + (MULTIPLE_OF_5 - 6 * SHIFT_5) + + (MULTIPLE_OF_7 - 9 * SHIFT_7) + //+ (MULTIPLE_OF_11 - 15 * SHIFT_11) + //+ (MULTIPLE_OF_13 - 18 * SHIFT_13) + //+ (MULTIPLE_OF_17 - 24 * SHIFT_17) + ; + factor = 3; + for (;;) { + /* The division is the most costly part of the loop. + * On 64bit CPUs, takes at best 12 cycles, often ~20. + */ + while ((N % factor) == 0) { /* not likely */ + N = N / factor; + printf(" %"HALF_FMT"u", factor); + max_factor = isqrt_odd(N); + } + next_factor: + if (factor >= max_factor) + break; + factor += 2; + /* Rudimentary wheel sieving: skip multiples of 3, 5 and 7: + * Every third odd number is divisible by three and thus isn't a prime: + * 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47... + * ^ ^ ^ ^ ^ ^ ^ _ ^ ^ _ ^ ^ ^ ^ + * (^ = primes, _ = would-be-primes-if-not-divisible-by-5) + * The numbers with space under them are excluded by sieve 3. + */ + // count7--; + // count5--; + // count3--; + // if (count3 && count5 && count7) + // continue; + sieve_word += INCREMENT_EACH; + if (!(sieve_word & MULTIPLE_DETECTED)) + continue; + /* + * "factor" is multiple of 3 33% of the time (count3 reached 0), + * else, multiple of 5 13% of the time, + * else, multiple of 7 7.6% of the time. + * Cumulatively, with 3,5,7 sieving we are here 54.3% of the time. + */ + // if (count3 == 0) + // count3 = 3; + if (sieve_word & MULTIPLE_OF_3) + sieve_word -= SHIFT_3 * 3; + // if (count5 == 0) + // count5 = 5; + if (sieve_word & MULTIPLE_OF_5) + sieve_word -= SHIFT_5 * 5; + // if (count7 == 0) + // count7 = 7; + if (sieve_word & MULTIPLE_OF_7) + sieve_word -= SHIFT_7 * 7; + goto next_factor; + } + end: + if (N > 1) + printf(" %llu", N); + bb_putchar('\n'); +} + +static void factorize_numstr(const char *numstr) +{ + wide_t N; + + /* Leading + is ok (coreutils compat) */ + if (*numstr == '+') + numstr++; + N = bb_strtoull(numstr, NULL, 10); + if (errno) + bb_show_usage(); + printf("%llu:", N); + factorize(N); +} + +int factor_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int factor_main(int argc UNUSED_PARAM, char **argv) +{ + //// coreutils has undocumented option ---debug (three dashes) + //getopt32(argv, ""); + //argv += optind; + argv++; + + if (!*argv) { + /* Read from stdin, several numbers per line are accepted */ + for (;;) { + char *numstr, *line; + line = xmalloc_fgetline(stdin); + if (!line) + return EXIT_SUCCESS; + numstr = line; + for (;;) { + char *end; + numstr = skip_whitespace(numstr); + if (!numstr[0]) + break; + end = skip_non_whitespace(numstr); + if (*end != '\0') + *end++ = '\0'; + factorize_numstr(numstr); + numstr = end; + } + free(line); + } + } + + do { + /* Leading spaces are ok (coreutils compat) */ + factorize_numstr(skip_whitespace(*argv)); + } while (*++argv); + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/false.c b/busybox/coreutils/false.c new file mode 100644 index 00000000000000..e3903efd091eeb --- /dev/null +++ b/busybox/coreutils/false.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini false implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config FALSE +//config: bool "false (tiny)" +//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 */ + +/* "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" +//usage: "1\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int false_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int false_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + return EXIT_FAILURE; +} diff --git a/busybox/coreutils/fold.c b/busybox/coreutils/fold.c new file mode 100644 index 00000000000000..1e26dde0c52f76 --- /dev/null +++ b/busybox/coreutils/fold.c @@ -0,0 +1,186 @@ +/* vi: set sw=4 ts=4: */ +/* + * fold -- wrap each input line to fit in specified width. + * + * Written by David MacKenzie, djm@gnu.ai.mit.edu. + * Copyright (C) 91, 1995-2002 Free Software Foundation, Inc. + * + * Modified for busybox based on coreutils v 5.0 + * Copyright (C) 2003 Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config FOLD +//config: bool "fold (4.6 kb)" +//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]..." +//usage:#define fold_full_usage "\n\n" +//usage: "Wrap input lines in each FILE (or stdin), writing to stdout\n" +//usage: "\n -b Count bytes rather than columns" +//usage: "\n -s Break at spaces" +//usage: "\n -w Use WIDTH columns instead of 80" + +#include "libbb.h" +#include "unicode.h" + +/* This is a NOEXEC applet. Be very careful! */ + +/* Must match getopt32 call */ +#define FLAG_COUNT_BYTES 1 +#define FLAG_BREAK_SPACES 2 +#define FLAG_WIDTH 4 + +/* Assuming the current column is COLUMN, return the column that + printing C will move the cursor to. + The first column is 0. */ +static int adjust_column(unsigned column, char c) +{ + if (option_mask32 & FLAG_COUNT_BYTES) + return ++column; + + if (c == '\t') + return column + 8 - column % 8; + + if (c == '\b') { + if ((int)--column < 0) + column = 0; + } + else if (c == '\r') + column = 0; + else { /* just a printable char */ + if (unicode_status != UNICODE_ON /* every byte is a new char */ + || (c & 0xc0) != 0x80 /* it isn't a 2nd+ byte of a Unicode char */ + ) { + column++; + } + } + return column; +} + +/* Note that this function can write NULs, unlike fputs etc. */ +static void write2stdout(const void *buf, unsigned size) +{ + fwrite(buf, 1, size, stdout); +} + +int fold_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fold_main(int argc UNUSED_PARAM, char **argv) +{ + char *line_out = NULL; + const char *w_opt = "80"; + unsigned width; + smallint exitcode = EXIT_SUCCESS; + + init_unicode(); + + if (ENABLE_INCLUDE_SUSv2) { + /* Turn any numeric options into -w options. */ + int i; + for (i = 1; argv[i]; i++) { + const char *a = argv[i]; + if (*a == '-') { + a++; + if (*a == '-' && !a[1]) /* "--" */ + break; + if (isdigit(*a)) + argv[i] = xasprintf("-w%s", a); + } + } + } + + getopt32(argv, "bsw:", &w_opt); + width = xatou_range(w_opt, 1, 10000); + + argv += optind; + if (!*argv) + *--argv = (char*)"-"; + + do { + FILE *istream = fopen_or_warn_stdin(*argv); + int c; + unsigned column = 0; /* Screen column where next char will go */ + unsigned offset_out = 0; /* Index in 'line_out' for next char */ + + if (istream == NULL) { + exitcode = EXIT_FAILURE; + continue; + } + + while ((c = getc(istream)) != EOF) { + /* We grow line_out in chunks of 0x1000 bytes */ + if ((offset_out & 0xfff) == 0) { + line_out = xrealloc(line_out, offset_out + 0x1000); + } + rescan: + line_out[offset_out] = c; + if (c == '\n') { + write2stdout(line_out, offset_out + 1); + column = offset_out = 0; + continue; + } + column = adjust_column(column, c); + if (column <= width || offset_out == 0) { + /* offset_out == 0 case happens + * with small width (say, 1) and tabs. + * The very first tab already goes to column 8, + * but we must not wrap it */ + offset_out++; + continue; + } + + /* This character would make the line too long. + * Print the line plus a newline, and make this character + * start the next line */ + if (option_mask32 & FLAG_BREAK_SPACES) { + unsigned i; + unsigned logical_end; + + /* Look for the last blank. */ + for (logical_end = offset_out - 1; (int)logical_end >= 0; logical_end--) { + if (!isblank(line_out[logical_end])) + continue; + + /* Found a space or tab. + * Output up to and including it, and start a new line */ + logical_end++; + /*line_out[logical_end] = '\n'; - NO! this nukes one buffered character */ + write2stdout(line_out, logical_end); + putchar('\n'); + /* Move the remainder to the beginning of the next line. + * The areas being copied here might overlap. */ + memmove(line_out, line_out + logical_end, offset_out - logical_end); + offset_out -= logical_end; + for (column = i = 0; i < offset_out; i++) { + column = adjust_column(column, line_out[i]); + } + goto rescan; + } + /* No blank found, wrap will split the overlong word */ + } + /* Output what we accumulated up to now, and start a new line */ + line_out[offset_out] = '\n'; + write2stdout(line_out, offset_out + 1); + column = offset_out = 0; + goto rescan; + } /* while (not EOF) */ + + if (offset_out) { + write2stdout(line_out, offset_out); + } + + if (fclose_if_not_stdin(istream)) { + bb_simple_perror_msg(*argv); + exitcode = EXIT_FAILURE; + } + } while (*++argv); + + fflush_stdout_and_exit(exitcode); +} diff --git a/busybox/coreutils/fsync.c b/busybox/coreutils/fsync.c new file mode 100644 index 00000000000000..5e77e2c1626554 --- /dev/null +++ b/busybox/coreutils/fsync.c @@ -0,0 +1,60 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini fsync implementation for busybox + * + * Copyright (C) 2008 Nokia Corporation. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config FSYNC +//config: bool "fsync (3.7 kb)" +//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..." +//usage:#define fsync_full_usage "\n\n" +//usage: "Write files' buffered blocks to disk\n" +//usage: "\n -d Avoid syncing metadata" + +#include "libbb.h" +#ifndef O_NOATIME +# define O_NOATIME 0 +#endif + +/* This is a NOFORK applet. Be very careful! */ + +int fsync_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fsync_main(int argc UNUSED_PARAM, char **argv) +{ + int status; + int opts; + + opts = getopt32(argv, "d"); /* fdatasync */ + argv += optind; + if (!*argv) { + bb_show_usage(); + } + + status = EXIT_SUCCESS; + do { + int fd = open_or_warn(*argv, O_NOATIME | O_NOCTTY | O_RDONLY); + + if (fd == -1) { + status = EXIT_FAILURE; + continue; + } + if ((opts ? fdatasync(fd) : fsync(fd))) { + //status = EXIT_FAILURE; - do we want this? + bb_simple_perror_msg(*argv); + } + close(fd); + } while (*++argv); + + return status; +} diff --git a/busybox/coreutils/head.c b/busybox/coreutils/head.c new file mode 100644 index 00000000000000..fc3a48d5ba832d --- /dev/null +++ b/busybox/coreutils/head.c @@ -0,0 +1,271 @@ +/* vi: set sw=4 ts=4: */ +/* + * head implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config HEAD +//config: bool "head (3.7 kb)" +//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 -c, -q, and -v" +//config: default y +//config: depends on HEAD + +//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 */ + +//usage:#define head_trivial_usage +//usage: "[OPTIONS] [FILE]..." +//usage:#define head_full_usage "\n\n" +//usage: "Print first 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 -n N[kbm] Print first N lines" +//usage: IF_FEATURE_FANCY_HEAD( +//usage: "\n -n -N[kbm] Print all except N last lines" +//usage: "\n -c [-]N[kbm] Print first N bytes" +//usage: "\n -q Never print headers" +//usage: "\n -v Always print headers" +//usage: ) +//usage: "\n" +//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)." +//usage: +//usage:#define head_example_usage +//usage: "$ head -n 2 /etc/passwd\n" +//usage: "root:x:0:0:root:/root:/bin/bash\n" +//usage: "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +#if !ENABLE_FEATURE_FANCY_HEAD +# define print_first_N(fp,count,bytes) print_first_N(fp,count) +#endif +static void +print_first_N(FILE *fp, unsigned long count, bool count_bytes) +{ +#if !ENABLE_FEATURE_FANCY_HEAD + const int count_bytes = 0; +#endif + while (count) { + int c = getc(fp); + if (c == EOF) + break; + if (count_bytes || (c == '\n')) + --count; + putchar(c); + } +} + +#if ENABLE_FEATURE_FANCY_HEAD +static void +print_except_N_last_bytes(FILE *fp, unsigned count) +{ + unsigned char *circle = xmalloc(++count); + unsigned head = 0; + for(;;) { + int c; + c = getc(fp); + if (c == EOF) + goto ret; + circle[head++] = c; + if (head == count) + break; + } + for (;;) { + int c; + if (head == count) + head = 0; + putchar(circle[head]); + c = getc(fp); + if (c == EOF) + goto ret; + circle[head] = c; + head++; + } + ret: + free(circle); +} + +static void +print_except_N_last_lines(FILE *fp, unsigned count) +{ + char **circle = xzalloc((++count) * sizeof(circle[0])); + unsigned head = 0; + for(;;) { + char *c; + c = xmalloc_fgets(fp); + if (!c) + goto ret; + circle[head++] = c; + if (head == count) + break; + } + for (;;) { + char *c; + if (head == count) + head = 0; + fputs(circle[head], stdout); + c = xmalloc_fgets(fp); + if (!c) + goto ret; + free(circle[head]); + circle[head++] = c; + } + ret: + head = 0; + for(;;) { + free(circle[head++]); + if (head == count) + break; + } + free(circle); +} +#else +/* Must never be called */ +void print_except_N_last_bytes(FILE *fp, unsigned count); +void print_except_N_last_lines(FILE *fp, unsigned count); +#endif + +#if !ENABLE_FEATURE_FANCY_HEAD +# define eat_num(negative_N,p) eat_num(p) +#endif +static unsigned long +eat_num(bool *negative_N, const char *p) +{ +#if ENABLE_FEATURE_FANCY_HEAD + if (*p == '-') { + *negative_N = 1; + p++; + } +#endif + return xatoul_sfx(p, bkm_suffixes); +} + +static const char head_opts[] ALIGN1 = + "n:" +#if ENABLE_FEATURE_FANCY_HEAD + "c:qv" +#endif + ; + +#define header_fmt_str "\n==> %s <==\n" + +int head_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int head_main(int argc, char **argv) +{ + unsigned long count = 10; +#if ENABLE_FEATURE_FANCY_HEAD + int header_threshhold = 1; + bool count_bytes = 0; + bool negative_N = 0; +#else +# define header_threshhold 1 +# define count_bytes 0 +# define negative_N 0 +#endif + FILE *fp; + const char *fmt; + char *p; + int opt; + int retval = EXIT_SUCCESS; + +#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD + /* Allow legacy syntax of an initial numeric option without -n. */ + if (argv[1] && argv[1][0] == '-' + && isdigit(argv[1][1]) + ) { + --argc; + ++argv; + p = argv[0] + 1; + goto GET_COUNT; + } +#endif + + /* No size benefit in converting this to getopt32 */ + while ((opt = getopt(argc, argv, head_opts)) > 0) { + switch (opt) { +#if ENABLE_FEATURE_FANCY_HEAD + case 'q': + header_threshhold = INT_MAX; + break; + case 'v': + header_threshhold = -1; + break; + case 'c': + count_bytes = 1; + /* fall through */ +#endif + case 'n': + p = optarg; +#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD + GET_COUNT: +#endif + count = eat_num(&negative_N, p); + break; + default: + bb_show_usage(); + } + } + + argc -= optind; + argv += optind; + if (!*argv) + *--argv = (char*)"-"; + + fmt = header_fmt_str + 1; + if (argc <= header_threshhold) { +#if ENABLE_FEATURE_FANCY_HEAD + header_threshhold = 0; +#else + fmt += 11; /* "" */ +#endif + } + if (negative_N) { + if (count >= INT_MAX / sizeof(char*)) + bb_error_msg("count is too big: %lu", count); + } + + do { + fp = fopen_or_warn_stdin(*argv); + if (fp) { + if (fp == stdin) { + *argv = (char *) bb_msg_standard_input; + } + if (header_threshhold) { + printf(fmt, *argv); + } + if (negative_N) { + if (count_bytes) { + print_except_N_last_bytes(fp, count); + } else { + print_except_N_last_lines(fp, count); + } + } else { + print_first_N(fp, count, count_bytes); + } + die_if_ferror_stdout(); + if (fclose_if_not_stdin(fp)) { + bb_simple_perror_msg(*argv); + retval = EXIT_FAILURE; + } + } else { + retval = EXIT_FAILURE; + } + fmt = header_fmt_str; + } while (*++argv); + + fflush_stdout_and_exit(retval); +} diff --git a/busybox/coreutils/hostid.c b/busybox/coreutils/hostid.c new file mode 100644 index 00000000000000..d21e6d6aca70e8 --- /dev/null +++ b/busybox/coreutils/hostid.c @@ -0,0 +1,42 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini hostid implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config HOSTID +//config: bool "hostid (247 bytes)" +//config: default y +//config: help +//config: hostid prints the numeric identifier (in hexadecimal) for +//config: the current host. + +//applet:IF_HOSTID(APPLET_NOFORK(hostid, hostid, BB_DIR_USR_BIN, BB_SUID_DROP, hostid)) + +//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" +//usage: "Print out a unique 32-bit identifier for the machine" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int hostid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int hostid_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + if (argv[1]) { + bb_show_usage(); + } + + /* POSIX says gethostid returns a "32-bit identifier" */ + printf("%08x\n", (unsigned)(uint32_t)gethostid()); + + return fflush_all(); +} diff --git a/busybox/coreutils/id.c b/busybox/coreutils/id.c new file mode 100644 index 00000000000000..5a7fb9aaf6b4b5 --- /dev/null +++ b/busybox/coreutils/id.c @@ -0,0 +1,269 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini id implementation for busybox + * + * Copyright (C) 2000 by Randolph Chung + * Copyright (C) 2008 by Tito Ragusa + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* 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 + * Added -G option Tito Ragusa (C) 2008 for SUSv3. + */ +//config:config ID +//config: bool "id (6.7 kb)" +//config: default y +//config: help +//config: id displays the current user and group ID names. +//config: +//config:config GROUPS +//config: bool "groups (6.5 kb)" +//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 + +/* BB_AUDIT SUSv3 compliant. */ + +//usage:#define id_trivial_usage +//usage: "[OPTIONS] [USER]" +//usage:#define id_full_usage "\n\n" +//usage: "Print information about USER or the current user\n" +//usage: IF_SELINUX( +//usage: "\n -Z Security context" +//usage: ) +//usage: "\n -u User ID" +//usage: "\n -g Group ID" +//usage: "\n -G Supplementary group IDs" +//usage: "\n -n Print names instead of numbers" +//usage: "\n -r Print real ID instead of effective ID" +//usage: +//usage:#define id_example_usage +//usage: "$ id\n" +//usage: "uid=1000(andersen) gid=1000(andersen)\n" + +//usage:#define groups_trivial_usage +//usage: "[USER]" +//usage:#define groups_full_usage "\n\n" +//usage: "Print the group memberships of USER or for the current process" +//usage: +//usage:#define groups_example_usage +//usage: "$ groups\n" +//usage: "andersen lp dialout cdrom floppy\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +#if !ENABLE_USE_BB_PWD_GRP +#if defined(__UCLIBC__) && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 30) +#error "Sorry, you need at least uClibc version 0.9.30 for id applet to build" +#endif +#endif + +enum { + PRINT_REAL = (1 << 0), + NAME_NOT_NUMBER = (1 << 1), + JUST_USER = (1 << 2), + JUST_GROUP = (1 << 3), + JUST_ALL_GROUPS = (1 << 4), +#if ENABLE_SELINUX + JUST_CONTEXT = (1 << 5), +#endif +}; + +static int print_common(unsigned id, const char *name, const char *prefix) +{ + if (prefix) { + printf("%s", prefix); + } + if (!(option_mask32 & NAME_NOT_NUMBER) || !name) { + printf("%u", id); + } + if (!option_mask32 || (option_mask32 & NAME_NOT_NUMBER)) { + if (name) { + printf(option_mask32 ? "%s" : "(%s)", name); + } else { + /* Don't set error status flag in default mode */ + if (option_mask32) { + if (ENABLE_DESKTOP) + bb_error_msg("unknown ID %u", id); + return EXIT_FAILURE; + } + } + } + return EXIT_SUCCESS; +} + +static int print_group(gid_t id, const char *prefix) +{ + return print_common(id, gid2group(id), prefix); +} + +static int print_user(uid_t id, const char *prefix) +{ + return print_common(id, uid2uname(id), prefix); +} + +/* On error set *n < 0 and return >= 0 + * If *n is too small, update it and return < 0 + * (ok to trash groups[] in both cases) + * Otherwise fill in groups[] and return >= 0 + */ +static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n) +{ + int m; + + if (username) { + /* If the user is a member of more than + * *n groups, then -1 is returned. Otherwise >= 0. + * (and no defined way of detecting errors?!) */ + m = getgrouplist(username, rgid, groups, n); + /* I guess *n < 0 might indicate error. Anyway, + * malloc'ing -1 bytes won't be good, so: */ + if (*n < 0) + return 0; + return m; + } + + *n = getgroups(*n, groups); + if (*n >= 0) + return *n; + /* Error */ + if (errno == EINVAL) /* *n is too small? */ + *n = getgroups(0, groups); /* get needed *n */ + /* if *n >= 0, return -1 (got new *n), else return 0 (error): */ + return -(*n >= 0); +} + +int id_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int id_main(int argc UNUSED_PARAM, char **argv) +{ + uid_t ruid; + gid_t rgid; + uid_t euid; + gid_t egid; + unsigned opt; + int i; + int status = EXIT_SUCCESS; + const char *prefix; + const char *username; +#if ENABLE_SELINUX + security_context_t scontext = NULL; +#endif + + if (ENABLE_GROUPS && (!ENABLE_ID || applet_name[0] == 'g')) { + /* TODO: coreutils groups prepend "USER : " prefix, + * and accept many usernames. Example: + * # groups root root + * root : root + * root : root + */ + opt = option_mask32 = getopt32(argv, "") | JUST_ALL_GROUPS | NAME_NOT_NUMBER; + } else { + /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/ + /* Don't allow more than one username */ + opt = getopt32(argv, "^" + "rnugG" IF_SELINUX("Z") + "\0" + "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG" + IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G") + ); + } + + username = argv[optind]; + if (username) { + struct passwd *p = xgetpwnam(username); + euid = ruid = p->pw_uid; + egid = rgid = p->pw_gid; + } else { + egid = getegid(); + rgid = getgid(); + euid = geteuid(); + ruid = getuid(); + } + /* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */ + /* id says: print the real ID instead of the effective ID, with -ugG */ + /* in fact in this case egid is always printed if egid != rgid */ + if (!opt || (opt & JUST_ALL_GROUPS)) { + gid_t *groups; + int n; + + if (!opt) { + /* Default Mode */ + status |= print_user(ruid, "uid="); + status |= print_group(rgid, " gid="); + if (euid != ruid) + status |= print_user(euid, " euid="); + if (egid != rgid) + status |= print_group(egid, " egid="); + } else { + /* JUST_ALL_GROUPS */ + status |= print_group(rgid, NULL); + if (egid != rgid) + status |= print_group(egid, " "); + } + /* We are supplying largish buffer, trying + * to not run get_groups() twice. That might be slow + * ("user database in remote SQL server" case) */ + groups = xmalloc(64 * sizeof(groups[0])); + n = 64; + if (get_groups(username, rgid, groups, &n) < 0) { + /* Need bigger buffer after all */ + groups = xrealloc(groups, n * sizeof(groups[0])); + get_groups(username, rgid, groups, &n); + } + if (n > 0) { + /* Print the list */ + prefix = " groups="; + for (i = 0; i < n; i++) { + if (opt && (groups[i] == rgid || groups[i] == egid)) + continue; + status |= print_group(groups[i], opt ? " " : prefix); + prefix = ","; + } + } else if (n < 0) { /* error in get_groups() */ + if (ENABLE_DESKTOP) + bb_error_msg_and_die("can't get groups"); + return EXIT_FAILURE; + } + if (ENABLE_FEATURE_CLEAN_UP) + free(groups); +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + if (getcon(&scontext) == 0) + printf(" context=%s", scontext); + } +#endif + } else if (opt & PRINT_REAL) { + euid = ruid; + egid = rgid; + } + + if (opt & JUST_USER) + status |= print_user(euid, NULL); + else if (opt & JUST_GROUP) + status |= print_group(egid, NULL); +#if ENABLE_SELINUX + else if (opt & JUST_CONTEXT) { + selinux_or_die(); + if (username || getcon(&scontext)) { + bb_error_msg_and_die("can't get process context%s", + username ? " for a different user" : ""); + } + fputs(scontext, stdout); + } + /* freecon(NULL) seems to be harmless */ + if (ENABLE_FEATURE_CLEAN_UP) + freecon(scontext); +#endif + bb_putchar('\n'); + fflush_stdout_and_exit(status); +} diff --git a/busybox/coreutils/id_test.sh b/busybox/coreutils/id_test.sh new file mode 100644 index 00000000000000..0d65f2ae31cdba --- /dev/null +++ b/busybox/coreutils/id_test.sh @@ -0,0 +1,244 @@ +#!/bin/bash +# Test script for busybox id vs. coreutils id. +# Needs root privileges for some tests. + +cp /usr/bin/id . +BUSYBOX=./busybox +ID=./id +LIST=`awk -F: '{ printf "%s\n", $1 }' /etc/passwd` +FLAG_USER_EXISTS="no" +TEST_USER="f583ca884c1d93458fb61ed137ff44f6" + +echo "test 1: id [options] nousername" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar +done + +echo "test 2: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + if test "$i" = "$TEST_USER"; then + FLAG_USER_EXISTS="yes" + fi + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +if test $FLAG_USER_EXISTS = "yes"; then + echo "test 3,4,5,6,7,8,9,10,11,12 skipped because test user $TEST_USER already exists" + rm -f foo bar + exit 1 +fi + +adduser -s /bin/true -g "" -H -D "$TEST_USER" || exit 1 + +chown $TEST_USER.$TEST_USER $BUSYBOX +chmod u+s $BUSYBOX 2>&1 /dev/null +chown $TEST_USER.$TEST_USER $ID +chmod u+s $ID 2>&1 /dev/null + +echo "test 3 setuid, existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 4 setuid, existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown $TEST_USER.$TEST_USER $BUSYBOX +chmod g+s $BUSYBOX 2>&1 /dev/null +chown $TEST_USER.$TEST_USER $ID +chmod g+s $ID 2>&1 /dev/null + +echo "test 5 setgid, existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 6 setgid, existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown $TEST_USER.$TEST_USER $BUSYBOX +chmod u+s,g+s $BUSYBOX 2>&1 /dev/null +chown $TEST_USER.$TEST_USER $ID +chmod u+s,g+s $ID 2>&1 /dev/null + +echo "test 7 setuid, setgid, existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 8 setuid, setgid, existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +deluser $TEST_USER || exit 1 + +echo "test 9 setuid, setgid, not existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar +done + +echo "test 10 setuid, setgid, not existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown .root $BUSYBOX 2>&1 /dev/null +chown .root $ID 2>&1 /dev/null +chmod g+s $BUSYBOX 2>&1 /dev/null +chmod g+s $ID 2>&1 /dev/null + +echo "test 11 setgid, not existing group: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 12 setgid, not existing group: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown root.root $BUSYBOX 2>&1 /dev/null +chown root.root $ID 2>&1 /dev/null +rm -f $ID +rm -f foo bar diff --git a/busybox/coreutils/install.c b/busybox/coreutils/install.c new file mode 100644 index 00000000000000..8270490bd47c00 --- /dev/null +++ b/busybox/coreutils/install.c @@ -0,0 +1,270 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 by Glenn McGrath + * SELinux support: by Yuichi Nakamura + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config INSTALL +//config: bool "install (12 kb)" +//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 + +//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] [-t DIR] [SOURCE]... DEST" +//usage:#define install_full_usage "\n\n" +//usage: "Copy files and set attributes\n" +//usage: "\n -c Just copy (default)" +//usage: "\n -d Create directories" +//usage: "\n -D Create leading target directories" +//usage: "\n -s Strip symbol table" +//usage: "\n -p Preserve date" +//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: ) + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +#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 + "context\0" Required_argument "Z" + "preserve_context\0" No_argument "\xff" + "preserve-context\0" No_argument "\xff" +# endif + ; +# define GETOPT32 getopt32long +# define LONGOPTS install_longopts, +#else +# define GETOPT32 getopt32 +# define LONGOPTS +#endif + + +#if ENABLE_SELINUX +static void setdefaultfilecon(const char *path) +{ + struct stat s; + security_context_t scontext = NULL; + + if (!is_selinux_enabled()) { + return; + } + if (lstat(path, &s) != 0) { + return; + } + + if (matchpathcon(path, s.st_mode, &scontext) < 0) { + goto out; + } + if (strcmp(scontext, "<>") == 0) { + goto out; + } + + if (lsetfilecon(path, scontext) < 0) { + if (errno != ENOTSUP) { + bb_perror_msg("warning: can't change context" + " of %s to %s", path, scontext); + } + } + + out: + freecon(scontext); +} + +#endif + +int install_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int install_main(int argc, char **argv) +{ + struct stat statbuf; + mode_t mode; + uid_t uid; + gid_t gid; + char *arg, *last; + 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 ret = EXIT_SUCCESS; + int isdir; +#if ENABLE_SELINUX + security_context_t scontext; + bool use_default_selinux_context = 1; +#endif + enum { + OPT_c = 1 << 0, + OPT_v = 1 << 1, + OPT_b = 1 << 2, + OPT_MKDIR_LEADING = 1 << 3, + OPT_DIRECTORY = 1 << 4, + OPT_PRESERVE_TIME = 1 << 5, + OPT_STRIP = 1 << 6, + OPT_GROUP = 1 << 7, + OPT_MODE = 1 << 8, + OPT_OWNER = 1 << 9, + OPT_TARGET = 1 << 10, +#if ENABLE_SELINUX + OPT_SET_SECURITY_CONTEXT = 1 << 11, + OPT_PRESERVE_SECURITY_CONTEXT = 1 << 12, +#endif + }; + + /* -c exists for backwards compatibility, it's needed */ + /* -b is ignored ("make a backup of each existing destination file") */ + opts = GETOPT32(argv, "^" + "cvb" "Ddpsg:m:o:t:" IF_SELINUX("Z:") + "\0" + "t--d:d--t:s--d:d--s" + IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z")), + LONGOPTS + &gid_str, &mode_str, &uid_str, &last + IF_SELINUX(, &scontext) + ); + argc -= optind; + argv += optind; + +#if ENABLE_SELINUX + if (opts & (OPT_PRESERVE_SECURITY_CONTEXT|OPT_SET_SECURITY_CONTEXT)) { + selinux_or_die(); + use_default_selinux_context = 0; + if (opts & OPT_PRESERVE_SECURITY_CONTEXT) { + copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; + } + if (opts & OPT_SET_SECURITY_CONTEXT) { + setfscreatecon_or_die(scontext); + copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT; + } + } +#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) { + copy_flags |= FILEUTILS_PRESERVE_STATUS; + } + mode = 0755; /* GNU coreutils 6.10 compat */ + if (opts & OPT_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(); + + /* 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 < 1) + bb_show_usage(); + + while ((arg = *argv++) != NULL) { + 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, mkdir_flags)) { + ret = EXIT_FAILURE; + goto next; + } + } else { + dest = last; + if (opts & OPT_MKDIR_LEADING) { + char *ddir = xstrdup(dest); + /* + * -D -t DIR1/DIR2/F3 FILE: create DIR1/DIR2/F3, copy FILE there + * -D FILE DIR1/DIR2/F3: create DIR1/DIR2, copy FILE there as F3 + */ + bb_make_directory((opts & OPT_TARGET) ? ddir : dirname(ddir), 0755, mkdir_flags); + /* errors are not checked. copy_file + * will fail if dir is not created. + */ + free(ddir); + } + if (isdir) + dest = concat_path_file(last, bb_basename(arg)); + if (copy_file(arg, dest, copy_flags) != 0) { + /* copy is not made */ + ret = EXIT_FAILURE; + goto next; + } + if (opts & OPT_STRIP) { + char *args[4]; + args[0] = (char*)"strip"; + args[1] = (char*)"-p"; /* -p --preserve-dates */ + args[2] = dest; + args[3] = NULL; + if (spawn_and_wait(args)) { + bb_perror_msg("strip"); + ret = EXIT_FAILURE; + } + } + } + + /* Set the file mode (always, not only with -m). + * GNU coreutils 6.10 is not affected by umask. */ + if (chmod(dest, mode) == -1) { + bb_perror_msg("can't change %s of %s", "permissions", dest); + ret = EXIT_FAILURE; + } +#if ENABLE_SELINUX + if (use_default_selinux_context) + setdefaultfilecon(dest); +#endif + /* Set the user and group id */ + if ((opts & (OPT_OWNER|OPT_GROUP)) + && lchown(dest, uid, gid) == -1 + ) { + bb_perror_msg("can't change %s of %s", "ownership", dest); + ret = EXIT_FAILURE; + } + next: + if (ENABLE_FEATURE_CLEAN_UP && isdir) + free(dest); + } + + return ret; +} diff --git a/busybox/coreutils/libcoreutils/Kbuild.src b/busybox/coreutils/libcoreutils/Kbuild.src new file mode 100644 index 00000000000000..2042d5f8493196 --- /dev/null +++ b/busybox/coreutils/libcoreutils/Kbuild.src @@ -0,0 +1,14 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 by Erik Andersen +# +# Licensed under GPLv2 or later, see file LICENSE in this source tree. + +lib-y:= + +INSERT +lib-$(CONFIG_MKFIFO) += getopt_mk_fifo_nod.o +lib-$(CONFIG_MKNOD) += getopt_mk_fifo_nod.o +lib-$(CONFIG_INSTALL) += cp_mv_stat.o +lib-$(CONFIG_CP) += cp_mv_stat.o +lib-$(CONFIG_MV) += cp_mv_stat.o diff --git a/busybox/coreutils/libcoreutils/coreutils.h b/busybox/coreutils/libcoreutils/coreutils.h new file mode 100644 index 00000000000000..ad102e4235ff5c --- /dev/null +++ b/busybox/coreutils/libcoreutils/coreutils.h @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +#ifndef COREUTILS_H +#define COREUTILS_H 1 + +PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN + +typedef int (*stat_func)(const char *fn, struct stat *ps); + +int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) FAST_FUNC; +int cp_mv_stat(const char *fn, struct stat *fn_stat) FAST_FUNC; + +mode_t getopt_mk_fifo_nod(char **argv) FAST_FUNC; + +POP_SAVED_FUNCTION_VISIBILITY + +#endif diff --git a/busybox/coreutils/libcoreutils/cp_mv_stat.c b/busybox/coreutils/libcoreutils/cp_mv_stat.c new file mode 100644 index 00000000000000..26c0e164566bea --- /dev/null +++ b/busybox/coreutils/libcoreutils/cp_mv_stat.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "libbb.h" +#include "coreutils.h" + +int FAST_FUNC cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) +{ + if (sf(fn, fn_stat) < 0) { + if (errno != ENOENT) { +#if ENABLE_FEATURE_VERBOSE_CP_MESSAGE + if (errno == ENOTDIR) { + bb_error_msg("can't stat '%s': Path has non-directory component", fn); + return -1; + } +#endif + bb_perror_msg("can't stat '%s'", fn); + return -1; + } + return 0; + } + if (S_ISDIR(fn_stat->st_mode)) { + return 3; + } + return 1; +} + +int FAST_FUNC cp_mv_stat(const char *fn, struct stat *fn_stat) +{ + return cp_mv_stat2(fn, fn_stat, stat); +} diff --git a/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c b/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c new file mode 100644 index 00000000000000..dafe70edfff5c8 --- /dev/null +++ b/busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "libbb.h" +#include "coreutils.h" + +mode_t FAST_FUNC getopt_mk_fifo_nod(char **argv) +{ + mode_t mode = 0666; + char *smode = NULL; +#if ENABLE_SELINUX + security_context_t scontext; +#endif + int opt; + opt = getopt32(argv, "m:" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext)); + if (opt & 1) { + 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); + } + +#if ENABLE_SELINUX + if (opt & 2) { + selinux_or_die(); + setfscreatecon_or_die(scontext); + } +#endif + + return mode; +} diff --git a/busybox/coreutils/link.c b/busybox/coreutils/link.c new file mode 100644 index 00000000000000..d8d583b7b79bd4 --- /dev/null +++ b/busybox/coreutils/link.c @@ -0,0 +1,39 @@ +/* + * link implementation for busybox + * + * Copyright (C) 2017 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config LINK +//config: bool "link (3.1 kb)" +//config: default y +//config: help +//config: link creates hard links between files. + +//applet:IF_LINK(APPLET_NOFORK(link, link, BB_DIR_BIN, BB_SUID_DROP, link)) + +//kbuild:lib-$(CONFIG_LINK) += link.o + +//usage:#define link_trivial_usage +//usage: "FILE LINK" +//usage:#define link_full_usage "\n\n" +//usage: "Create hard LINK to FILE" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int link_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int link_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, "^" "" "\0" "=2"); + argv += optind; + if (link(argv[0], argv[1]) != 0) { + /* shared message */ + bb_perror_msg_and_die("can't create %slink '%s' to '%s'", + "hard", argv[1], argv[0] + ); + } + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/ln.c b/busybox/coreutils/ln.c new file mode 100644 index 00000000000000..2dda5dae9b187d --- /dev/null +++ b/busybox/coreutils/ln.c @@ -0,0 +1,148 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ln implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config LN +//config: bool "ln (4.5 kb)" +//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. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */ + +//usage:#define ln_trivial_usage +//usage: "[OPTIONS] TARGET... LINK|DIR" +//usage:#define ln_full_usage "\n\n" +//usage: "Create a link LINK or DIR/TARGET to the specified TARGET(s)\n" +//usage: "\n -s Make symlinks instead of hardlinks" +//usage: "\n -f Remove existing destinations" +//usage: "\n -n Don't dereference symlinks - treat like normal file" +//usage: "\n -b Make a backup of the target (if exists) before link operation" +//usage: "\n -S suf Use suffix instead of ~ when making backup files" +//usage: "\n -T 2nd arg must be a DIR" +//usage: "\n -v Verbose" +//usage: +//usage:#define ln_example_usage +//usage: "$ ln -s BusyBox /tmp/ls\n" +//usage: "$ ls -l /tmp/ls\n" +//usage: "lrwxrwxrwx 1 root root 7 Apr 12 18:39 ls -> BusyBox*\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +#define LN_SYMLINK (1 << 0) +#define LN_FORCE (1 << 1) +#define LN_NODEREFERENCE (1 << 2) +#define LN_BACKUP (1 << 3) +#define LN_SUFFIX (1 << 4) +#define LN_VERBOSE (1 << 5) +#define LN_LINKFILE (1 << 6) + +int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ln_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int opts; + char *last; + char *src_name; + char *src; + char *suffix = (char*)"~"; + struct stat statbuf; + int (*link_func)(const char *, const char *); + + opts = getopt32(argv, "^" "sfnbS:vT" "\0" "-1", &suffix); + + last = argv[argc - 1]; + argv += optind; + argc -= optind; + + if ((opts & LN_LINKFILE) && argc > 2) { + bb_error_msg_and_die("-T accepts 2 args max"); + } + + if (!argv[1]) { + /* "ln PATH/TO/FILE" -> "ln PATH/TO/FILE FILE" */ + *--argv = last; + /* xstrdup is needed: "ln -s PATH/TO/FILE/" is equivalent to + * "ln -s PATH/TO/FILE/ FILE", not "ln -s PATH/TO/FILE FILE" + */ + last = bb_get_last_path_component_strip(xstrdup(last)); + } + + do { + src_name = NULL; + src = last; + + if (is_directory(src, + (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE + ) + ) { + if (opts & LN_LINKFILE) { + bb_error_msg_and_die("'%s' is a directory", src); + } + src_name = xstrdup(*argv); + src = concat_path_file(src, bb_get_last_path_component_strip(src_name)); + free(src_name); + src_name = src; + } + if (!(opts & LN_SYMLINK) && stat(*argv, &statbuf)) { + // coreutils: "ln dangling_symlink new_hardlink" works + if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) { + bb_simple_perror_msg(*argv); + status = EXIT_FAILURE; + free(src_name); + continue; + } + } + + if (opts & LN_BACKUP) { + char *backup; + backup = xasprintf("%s%s", src, suffix); + if (rename(src, backup) < 0 && errno != ENOENT) { + bb_simple_perror_msg(src); + status = EXIT_FAILURE; + free(backup); + continue; + } + free(backup); + /* + * When the source and dest are both hard links to the same + * inode, a rename may succeed even though nothing happened. + * Therefore, always unlink(). + */ + unlink(src); + } else if (opts & LN_FORCE) { + unlink(src); + } + + link_func = link; + if (opts & LN_SYMLINK) { + link_func = symlink; + } + + if (opts & LN_VERBOSE) { + printf("'%s' -> '%s'\n", src, *argv); + } + + if (link_func(*argv, src) != 0) { + bb_simple_perror_msg(src); + status = EXIT_FAILURE; + } + + free(src_name); + } while ((++argv)[1]); + + return status; +} diff --git a/busybox/coreutils/logname.c b/busybox/coreutils/logname.c new file mode 100644 index 00000000000000..e614ca7cf5bdf4 --- /dev/null +++ b/busybox/coreutils/logname.c @@ -0,0 +1,60 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini logname implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * SUSv3 specifies the string used is that returned from getlogin(). + * The previous implementation used getpwuid() for geteuid(), which + * is _not_ the same. Erik apparently made this change almost 3 years + * ago to avoid failing when no utmp was available. However, the + * correct course of action wrt SUSv3 for a failing getlogin() is + * a diagnostic message and an error return. + */ +//config:config LOGNAME +//config: bool "logname (894 bytes)" +//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: "" +//usage:#define logname_full_usage "\n\n" +//usage: "Print the name of the current user" +//usage: +//usage:#define logname_example_usage +//usage: "$ logname\n" +//usage: "root\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int logname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int logname_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + char buf[64]; + + if (argv[1]) { + bb_show_usage(); + } + + /* Using _r function - avoid pulling in static buffer from libc */ + if (getlogin_r(buf, sizeof(buf)) == 0) { + puts(buf); + return fflush_all(); + } + + bb_perror_msg_and_die("getlogin"); +} diff --git a/busybox/coreutils/ls.c b/busybox/coreutils/ls.c new file mode 100644 index 00000000000000..4be499088062b8 --- /dev/null +++ b/busybox/coreutils/ls.c @@ -0,0 +1,1252 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 1996 Brian Candler + * + * 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 + * (i.e., the ones I couldn't live without :-) All features which involve + * linking in substantial chunks of libc can be disabled. + * + * Although I don't really want to add new features to this program to + * keep it small, I *am* interested to receive bug fixes and ways to make + * it more portable. + * + * KNOWN BUGS: + * 1. hidden files can make column width too large + * + * NON-OPTIMAL BEHAVIOUR: + * 1. autowidth reads directories twice + * 2. if you do a short directory listing without filetype characters + * appended, there's no need to stat each one + * PORTABILITY: + * 1. requires lstat (BSD) - how do you do it without? + * + * [2009-03] + * ls sorts listing now, and supports almost all options. + */ +//config:config LS +//config: bool "ls (14 kb)" +//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: +//config:config FEATURE_LS_FOLLOWLINKS +//config: bool "Enable symlinks dereferencing (-L)" +//config: default y +//config: depends on LS +//config: +//config:config FEATURE_LS_RECURSIVE +//config: bool "Enable recursion (-R)" +//config: default y +//config: depends on LS +//config: +//config:config FEATURE_LS_WIDTH +//config: bool "Enable -w WIDTH and window size autodetection" +//config: default y +//config: depends on LS +//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" +//usage: IF_FEATURE_LS_FOLLOWLINKS("LH") +//usage: IF_FEATURE_LS_RECURSIVE("R") +//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins" +//usage: IF_FEATURE_HUMAN_READABLE("h") +//usage: IF_FEATURE_LS_SORTFILES("rSXv") +//usage: IF_FEATURE_LS_TIMESTAMPS("ctu") +//usage: IF_SELINUX("kZ") "]" +//usage: IF_FEATURE_LS_WIDTH(" [-w WIDTH]") " [FILE]..." +//usage:#define ls_full_usage "\n\n" +//usage: "List directory contents\n" +//usage: "\n -1 One column output" +//usage: "\n -a Include entries which start with ." +//usage: "\n -A Like -a, but exclude . and .." +////usage: "\n -C List by columns" - don't show, this is a default anyway +//usage: "\n -x List by lines" +//usage: "\n -d List directory entries instead of contents" +//usage: IF_FEATURE_LS_FOLLOWLINKS( +//usage: "\n -L Follow symlinks" +//usage: "\n -H Follow symlinks on command line" +//usage: ) +//usage: IF_FEATURE_LS_RECURSIVE( +//usage: "\n -R Recurse" +//usage: ) +//usage: IF_FEATURE_LS_FILETYPES( +//usage: "\n -p Append / to dir entries" +//usage: "\n -F Append indicator (one of */=@|) to entries" +//usage: ) +//usage: "\n -l Long listing format" +//usage: "\n -i List inode numbers" +//usage: "\n -n List numeric UIDs and GIDs instead of names" +//usage: "\n -s List allocated blocks" +//usage: IF_FEATURE_LS_TIMESTAMPS( +//usage: "\n -lc List ctime" +//usage: "\n -lu List atime" +//usage: ) +//usage: IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS( +//usage: "\n --full-time List full date and time" +//usage: )) +//usage: IF_FEATURE_HUMAN_READABLE( +//usage: "\n -h Human readable sizes (1K 243M 2G)" +//usage: ) +//usage: IF_FEATURE_LS_SORTFILES( +//usage: IF_LONG_OPTS( +//usage: "\n --group-directories-first" +//usage: ) +//usage: "\n -S Sort by size" +//usage: "\n -X Sort by extension" +//usage: "\n -v Sort by version" +//usage: ) +//usage: IF_FEATURE_LS_TIMESTAMPS( +//usage: "\n -t Sort by mtime" +//usage: "\n -tc Sort by ctime" +//usage: "\n -tu Sort by atime" +//usage: ) +//usage: "\n -r Reverse sort order" +//usage: IF_SELINUX( +//usage: "\n -Z List security context and permission" +//usage: ) +//usage: IF_FEATURE_LS_WIDTH( +//usage: "\n -w N Format N columns wide" +//usage: ) +//usage: IF_FEATURE_LS_COLOR( +//usage: "\n --color[={always,never,auto}] Control coloring" +//usage: ) + +#include "libbb.h" +#include "common_bufsiz.h" +#include "unicode.h" + + +/* This is a NOEXEC applet. Be very careful! */ + + +#if ENABLE_FTPD +/* ftpd uses ls, and without timestamps Mozilla won't understand + * ftpd's LIST output. + */ +# undef CONFIG_FEATURE_LS_TIMESTAMPS +# undef ENABLE_FEATURE_LS_TIMESTAMPS +# undef IF_FEATURE_LS_TIMESTAMPS +# undef IF_NOT_FEATURE_LS_TIMESTAMPS +# define CONFIG_FEATURE_LS_TIMESTAMPS 1 +# define ENABLE_FEATURE_LS_TIMESTAMPS 1 +# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__ +# define IF_NOT_FEATURE_LS_TIMESTAMPS(...) +#endif + + +enum { +TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ + +SPLIT_FILE = 0, +SPLIT_DIR = 1, +SPLIT_SUBDIR = 2, +}; + +/* -Cadi1l Std options, busybox always supports */ +/* -gnsxA Std options, busybox always supports */ +/* -Q GNU option, busybox always supports */ +/* -k Std option, busybox always supports (by ignoring) */ +/* It means "for -s, show sizes in kbytes" */ +/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */ +/* since otherwise -s shows kbytes anyway */ +/* -LHRctur Std options, busybox optionally supports */ +/* -Fp Std options, busybox optionally supports */ +/* -SXvhTw GNU options, busybox optionally supports */ +/* -T WIDTH Ignored (we don't use tabs on output) */ +/* -Z SELinux mandated option, busybox optionally supports */ +#define ls_options \ + "Cadi1lgnsxAk" /* 12 opts, total 12 */ \ + IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \ + IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \ + IF_SELINUX("Z") /* 1, 16 */ \ + "Q" /* 1, 17 */ \ + IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \ + IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \ + IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \ + IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \ + IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */ + +enum { + OPT_C = (1 << 0), + OPT_a = (1 << 1), + OPT_d = (1 << 2), + OPT_i = (1 << 3), + OPT_1 = (1 << 4), + OPT_l = (1 << 5), + OPT_g = (1 << 6), + OPT_n = (1 << 7), + OPT_s = (1 << 8), + OPT_x = (1 << 9), + OPT_A = (1 << 10), + //OPT_k = (1 << 11), + + OPTBIT_F = 12, + OPTBIT_p, /* 13 */ + OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES, + OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE, + OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX, + OPTBIT_c, /* 17 */ + OPTBIT_t, /* 18 */ + OPTBIT_u, /* 19 */ + OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS, + OPTBIT_X, /* 21 */ + OPTBIT_r, /* 22 */ + OPTBIT_v, /* 23 */ + OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES, + OPTBIT_H, /* 25 */ + OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS, + OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE, + OPTBIT_w, /* 28 */ + OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH, + OPTBIT_dirs_first, + OPTBIT_color, /* 31 */ + /* with long opts, we use all 32 bits */ + + OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES, + OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES, + OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE, + OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX, + OPT_Q = (1 << OPTBIT_Q), + OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS, + OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS, + OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS, + OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES, + OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES, + OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES, + OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES, + OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS, + OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS, + OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE, + OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_LS_WIDTH, + OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_LS_WIDTH, + OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS, + OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS, + OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR, +}; + +/* + * a directory entry and its stat info + */ +struct dnode { + const char *name; /* usually basename, but think "ls -l dir/file" */ + const char *fullname; /* full name (usable for stat etc) */ + struct dnode *dn_next; /* for linked list */ + IF_SELINUX(security_context_t sid;) + smallint fname_allocated; + + /* Used to avoid re-doing [l]stat at printout stage + * if we already collected needed data in scan stage: + */ + mode_t dn_mode_lstat; /* obtained with lstat, or 0 */ + mode_t dn_mode_stat; /* obtained with stat, or 0 */ + +// struct stat dstat; +// struct stat is huge. We don't need it in full. +// At least we don't need st_dev and st_blksize, +// but there are invisible fields as well +// (such as nanosecond-resolution timespamps) +// and padding, which we also don't want to store. +// We also can pre-parse dev_t dn_rdev (in glibc, it's huge). +// On 32-bit uclibc: dnode size went from 112 to 84 bytes. +// + /* Same names as in struct stat, but with dn_ instead of st_ pfx: */ + mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */ + off_t dn_size; +#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES + time_t dn_time; +#endif + ino_t dn_ino; + blkcnt_t dn_blocks; + nlink_t dn_nlink; + uid_t dn_uid; + gid_t dn_gid; + int dn_rdev_maj; + int dn_rdev_min; +// dev_t dn_dev; +// blksize_t dn_blksize; +}; + +struct globals { +#if ENABLE_FEATURE_LS_COLOR + smallint show_color; +# define G_show_color (G.show_color) +#else +# define G_show_color 0 +#endif + smallint exit_code; + smallint show_dirname; +#if ENABLE_FEATURE_LS_WIDTH + unsigned terminal_width; +# define G_terminal_width (G.terminal_width) +#else +# define G_terminal_width TERMINAL_WIDTH +#endif +#if ENABLE_FEATURE_LS_TIMESTAMPS + /* Do time() just once. Saves one syscall per file for "ls -l" */ + time_t current_time_t; +#endif +} FIX_ALIASING; +#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_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \ + IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \ +} while (0) + +#define ESC "\033" + + +/*** Output code ***/ + + +/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket + * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file + * 3/7:multiplexed char/block device) + * and we use 0 for unknown and 15 for executables (see below) */ +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +/* un fi chr - dir - blk - file - link - sock - - exe */ +#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)]) +/* 036 black foreground 050 black background + 037 red foreground 051 red background + 040 green foreground 052 green background + 041 brown foreground 053 brown background + 042 blue foreground 054 blue background + 043 magenta (purple) foreground 055 magenta background + 044 cyan (light blue) foreground 056 cyan background + 045 gray foreground 057 white background +*/ +#define COLOR(mode) ( \ + /*un fi chr - dir - blk - file - link - sock - - exe */ \ + "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \ + [TYPEINDEX(mode)]) +/* Select normal (0) [actually "reset all"] or bold (1) + * (other attributes are 2:dim 4:underline 5:blink 7:reverse, + * let's use 7 for "impossible" types, just for fun) + * Note: coreutils 6.9 uses inverted red for setuid binaries. + */ +#define ATTR(mode) ( \ + /*un fi chr - dir - blk - file- link- sock- - exe */ \ + "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \ + [TYPEINDEX(mode)]) + +#if ENABLE_FEATURE_LS_COLOR +/* mode of zero is interpreted as "unknown" (stat failed) */ +static char fgcolor(mode_t mode) +{ + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return COLOR(0xF000); /* File is executable ... */ + return COLOR(mode); +} +static char bold(mode_t mode) +{ + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return ATTR(0xF000); /* File is executable ... */ + return ATTR(mode); +} +#endif + +#if ENABLE_FEATURE_LS_FILETYPES +static char append_char(mode_t mode) +{ + if (!(option_mask32 & (OPT_F|OPT_p))) + return '\0'; + + if (S_ISDIR(mode)) + return '/'; + if (!(option_mask32 & OPT_F)) + return '\0'; + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return '*'; + return APPCHAR(mode); +} +#endif + +static unsigned calc_name_len(const char *name) +{ + unsigned len; + uni_stat_t uni_stat; + + // TODO: quote tab as \t, etc, if -Q + name = printable_string(&uni_stat, name); + + if (!(option_mask32 & OPT_Q)) { + return uni_stat.unicode_width; + } + + len = 2 + uni_stat.unicode_width; + while (*name) { + if (*name == '"' || *name == '\\') { + len++; + } + name++; + } + return len; +} + +/* Return the number of used columns. + * Note that only columnar output uses return value. + * -l and -1 modes don't care. + * coreutils 7.2 also supports: + * ls -b (--escape) = octal escapes (although it doesn't look like working) + * ls -N (--literal) = not escape at all + */ +static unsigned print_name(const char *name) +{ + unsigned len; + uni_stat_t uni_stat; + + // TODO: quote tab as \t, etc, if -Q + name = printable_string(&uni_stat, name); + + if (!(option_mask32 & OPT_Q)) { + fputs(name, stdout); + return uni_stat.unicode_width; + } + + len = 2 + uni_stat.unicode_width; + putchar('"'); + while (*name) { + if (*name == '"' || *name == '\\') { + putchar('\\'); + len++; + } + putchar(*name); + name++; + } + putchar('"'); + return len; +} + +/* Return the number of used columns. + * Note that only columnar output uses return value, + * -l and -1 modes don't care. + */ +static NOINLINE unsigned display_single(const struct dnode *dn) +{ + unsigned column = 0; + char *lpath; + int opt; +#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR + struct stat statbuf; +#endif +#if ENABLE_FEATURE_LS_FILETYPES + char append = append_char(dn->dn_mode); +#endif + + opt = option_mask32; + + /* Do readlink early, so that if it fails, error message + * does not appear *inside* the "ls -l" line */ + lpath = NULL; + if (opt & OPT_l) + if (S_ISLNK(dn->dn_mode)) + lpath = xmalloc_readlink_or_warn(dn->fullname); + + if (opt & OPT_i) /* show inode# */ + column += printf("%7llu ", (long long) dn->dn_ino); +//TODO: -h should affect -s too: + if (opt & OPT_s) /* show allocated blocks */ + column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); + if (opt & OPT_l) { + /* long listing: show mode */ + column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode)); + /* long listing: show number of links */ + column += printf("%4lu ", (long) dn->dn_nlink); + /* long listing: show user/group */ + if (opt & OPT_n) { + if (opt & OPT_g) + column += printf("%-8u ", (int) dn->dn_gid); + else + column += printf("%-8u %-8u ", + (int) dn->dn_uid, + (int) dn->dn_gid); + } +#if ENABLE_FEATURE_LS_USERNAME + else { + if (opt & OPT_g) { + column += printf("%-8.8s ", + get_cached_groupname(dn->dn_gid)); + } else { + column += printf("%-8.8s %-8.8s ", + get_cached_username(dn->dn_uid), + get_cached_groupname(dn->dn_gid)); + } + } +#endif +#if ENABLE_SELINUX + } + if (opt & OPT_Z) { + column += printf("%-32s ", dn->sid ? dn->sid : "?"); + freecon(dn->sid); + } + if (opt & OPT_l) { +#endif + /* long listing: show size */ + if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) { + column += printf("%4u, %3u ", + dn->dn_rdev_maj, + dn->dn_rdev_min); + } else { + if (opt & OPT_h) { + column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ", + /* print size, show one fractional, use suffixes */ + make_human_readable_str(dn->dn_size, 1, 0) + ); + } else { + column += printf("%9"OFF_FMT"u ", dn->dn_size); + } + } +#if ENABLE_FEATURE_LS_TIMESTAMPS + /* long listing: show {m,c,a}time */ + if (opt & OPT_full_time) { /* --full-time */ + /* coreutils 8.4 ls --full-time prints: + * 2009-07-13 17:49:27.000000000 +0200 + * we don't show fractional seconds. + */ + char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")]; + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z", + localtime(&dn->dn_time)); + column += printf("%s ", buf); + } else { /* ordinary time format */ + /* G.current_time_t is ~== time(NULL) */ + char *filetime = ctime(&dn->dn_time); + /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */ + time_t age = G.current_time_t - dn->dn_time; + if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { + /* 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; + } +#endif + } + +#if ENABLE_FEATURE_LS_COLOR + if (G_show_color) { + mode_t mode = dn->dn_mode_lstat; + if (!mode) + if (lstat(dn->fullname, &statbuf) == 0) + mode = statbuf.st_mode; + printf(ESC"[%u;%um", bold(mode), fgcolor(mode)); + } +#endif + column += print_name(dn->name); + if (G_show_color) { + printf(ESC"[m"); + } + + if (lpath) { + printf(" -> "); +#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR + if ((opt & (OPT_F|OPT_p)) + || G_show_color + ) { + mode_t mode = dn->dn_mode_stat; + if (!mode) + if (stat(dn->fullname, &statbuf) == 0) + mode = statbuf.st_mode; +# if ENABLE_FEATURE_LS_FILETYPES + append = append_char(mode); +# endif +# if ENABLE_FEATURE_LS_COLOR + if (G_show_color) { + printf(ESC"[%u;%um", bold(mode), fgcolor(mode)); + } +# endif + } +#endif + column += print_name(lpath) + 4; + free(lpath); + if (G_show_color) { + printf(ESC"[m"); + } + } +#if ENABLE_FEATURE_LS_FILETYPES + if (opt & (OPT_F|OPT_p)) { + if (append) { + putchar(append); + column++; + } + } +#endif + + return column; +} + +static void display_files(struct dnode **dn, unsigned nfiles) +{ + unsigned i, ncols, nrows, row, nc; + unsigned column; + unsigned nexttab; + unsigned column_width = 0; /* used only by coulmnal output */ + + if (option_mask32 & (OPT_l|OPT_1)) { + ncols = 1; + } else { + /* find the longest file name, use that as the column width */ + for (i = 0; dn[i]; i++) { + int len = calc_name_len(dn[i]->name); + if (column_width < len) + column_width = len; + } + column_width += 2 + + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */ + + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */ + + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */ + ; + ncols = (unsigned)G_terminal_width / column_width; + } + + if (ncols > 1) { + nrows = nfiles / ncols; + if (nrows * ncols < nfiles) + nrows++; /* round up fractionals */ + } else { + nrows = nfiles; + ncols = 1; + } + + column = 0; + nexttab = 0; + for (row = 0; row < nrows; row++) { + for (nc = 0; nc < ncols; nc++) { + /* reach into the array based on the column and row */ + if (option_mask32 & OPT_x) + i = (row * ncols) + nc; /* display across row */ + else + i = (nc * nrows) + row; /* display by column */ + if (i < nfiles) { + if (column > 0) { + nexttab -= column; + printf("%*s", nexttab, ""); + column += nexttab; + } + nexttab = column + column_width; + column += display_single(dn[i]); + } + } + putchar('\n'); + column = 0; + } +} + + +/*** Dir scanning code ***/ + +static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) +{ + struct stat statbuf; + struct dnode *cur; + + cur = xzalloc(sizeof(*cur)); + cur->fullname = fullname; + cur->name = name; + + if ((option_mask32 & OPT_L) || force_follow) { +#if ENABLE_SELINUX + if (option_mask32 & OPT_Z) { + getfilecon(fullname, &cur->sid); + } +#endif + if (stat(fullname, &statbuf)) { + bb_simple_perror_msg(fullname); + G.exit_code = EXIT_FAILURE; + free(cur); + return NULL; + } + cur->dn_mode_stat = statbuf.st_mode; + } else { +#if ENABLE_SELINUX + if (option_mask32 & OPT_Z) { + lgetfilecon(fullname, &cur->sid); + } +#endif + if (lstat(fullname, &statbuf)) { + bb_simple_perror_msg(fullname); + G.exit_code = EXIT_FAILURE; + free(cur); + return NULL; + } + cur->dn_mode_lstat = statbuf.st_mode; + } + + /* cur->dstat = statbuf: */ + cur->dn_mode = statbuf.st_mode ; + cur->dn_size = statbuf.st_size ; +#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES + cur->dn_time = statbuf.st_mtime ; + if (option_mask32 & OPT_u) + cur->dn_time = statbuf.st_atime; + if (option_mask32 & OPT_c) + cur->dn_time = statbuf.st_ctime; +#endif + cur->dn_ino = statbuf.st_ino ; + cur->dn_blocks = statbuf.st_blocks; + cur->dn_nlink = statbuf.st_nlink ; + cur->dn_uid = statbuf.st_uid ; + cur->dn_gid = statbuf.st_gid ; + cur->dn_rdev_maj = major(statbuf.st_rdev); + cur->dn_rdev_min = minor(statbuf.st_rdev); + + return cur; +} + +static unsigned count_dirs(struct dnode **dn, int which) +{ + unsigned dirs, all; + + if (!dn) + return 0; + + dirs = all = 0; + for (; *dn; dn++) { + const char *name; + + all++; + if (!S_ISDIR((*dn)->dn_mode)) + continue; + + name = (*dn)->name; + if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */ + /* or if it's not . or .. */ + || name[0] != '.' + || (name[1] && (name[1] != '.' || name[2])) + ) { + dirs++; + } + } + return which != SPLIT_FILE ? dirs : all - dirs; +} + +/* get memory to hold an array of pointers */ +static struct dnode **dnalloc(unsigned num) +{ + if (num < 1) + return NULL; + + num++; /* so that we have terminating NULL */ + return xzalloc(num * sizeof(struct dnode *)); +} + +#if ENABLE_FEATURE_LS_RECURSIVE +static void dfree(struct dnode **dnp) +{ + unsigned i; + + if (dnp == NULL) + return; + + for (i = 0; dnp[i]; i++) { + struct dnode *cur = dnp[i]; + if (cur->fname_allocated) + free((char*)cur->fullname); + free(cur); + } + free(dnp); +} +#else +#define dfree(...) ((void)0) +#endif + +/* Returns NULL-terminated malloced vector of pointers (or NULL) */ +static struct dnode **splitdnarray(struct dnode **dn, int which) +{ + unsigned dncnt, d; + struct dnode **dnp; + + if (dn == NULL) + return NULL; + + /* count how many dirs or files there are */ + dncnt = count_dirs(dn, which); + + /* allocate a file array and a dir array */ + dnp = dnalloc(dncnt); + + /* copy the entrys into the file or dir array */ + for (d = 0; *dn; dn++) { + if (S_ISDIR((*dn)->dn_mode)) { + const char *name; + + if (which == SPLIT_FILE) + continue; + + name = (*dn)->name; + if ((which & SPLIT_DIR) /* any dir... */ + /* ... or not . or .. */ + || name[0] != '.' + || (name[1] && (name[1] != '.' || name[2])) + ) { + dnp[d++] = *dn; + } + } else + if (which == SPLIT_FILE) { + dnp[d++] = *dn; + } + } + return dnp; +} + +#if ENABLE_FEATURE_LS_SORTFILES +static int sortcmp(const void *a, const void *b) +{ + struct dnode *d1 = *(struct dnode **)a; + struct dnode *d2 = *(struct dnode **)b; + unsigned opt = option_mask32; + off_t dif; + + dif = 0; /* assume sort by name */ + // TODO: use pre-initialized function pointer + // instead of branch forest + if (opt & OPT_dirs_first) { + dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode); + if (dif != 0) + goto maybe_invert_and_ret; + } + + if (opt & OPT_S) { /* sort by size */ + dif = (d2->dn_size - d1->dn_size); + } else + if (opt & OPT_t) { /* sort by time */ + dif = (d2->dn_time - d1->dn_time); + } else +#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1 + if (opt & OPT_v) { /* sort by version */ + dif = strverscmp(d1->name, d2->name); + } else +#endif + if (opt & OPT_X) { /* sort by extension */ + dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.')); + } + if (dif == 0) { + /* sort by name, use as tie breaker for other sorts */ + if (ENABLE_LOCALE_SUPPORT) + dif = strcoll(d1->name, d2->name); + else + dif = strcmp(d1->name, d2->name); + } else { + /* Make dif fit into an int */ + if (sizeof(dif) > sizeof(int)) { + enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) }; + /* shift leaving only "int" worth of bits */ + /* (this requires dif != 0, and here it is nonzero) */ + dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT); + } + } + maybe_invert_and_ret: + return (opt & OPT_r) ? -(int)dif : (int)dif; +} + +static void dnsort(struct dnode **dn, int size) +{ + qsort(dn, size, sizeof(*dn), sortcmp); +} + +static void sort_and_display_files(struct dnode **dn, unsigned nfiles) +{ + dnsort(dn, nfiles); + display_files(dn, nfiles); +} +#else +# define dnsort(dn, size) ((void)0) +# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles) +#endif + +/* Returns NULL-terminated malloced vector of pointers (or NULL) */ +static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p) +{ + struct dnode *dn, *cur, **dnp; + struct dirent *entry; + DIR *dir; + unsigned i, nfiles; + + *nfiles_p = 0; + dir = warn_opendir(path); + if (dir == NULL) { + G.exit_code = EXIT_FAILURE; + return NULL; /* could not open the dir */ + } + dn = NULL; + nfiles = 0; + while ((entry = readdir(dir)) != NULL) { + char *fullname; + + /* are we going to list the file- it may be . or .. or a hidden file */ + if (entry->d_name[0] == '.') { + if (!(option_mask32 & (OPT_a|OPT_A))) + continue; /* skip all dotfiles if no -a/-A */ + if (!(option_mask32 & OPT_a) + && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) + ) { + continue; /* if only -A, skip . and .. but show other dotfiles */ + } + } + fullname = concat_path_file(path, entry->d_name); + cur = my_stat(fullname, bb_basename(fullname), 0); + if (!cur) { + free(fullname); + continue; + } + cur->fname_allocated = 1; + cur->dn_next = dn; + dn = cur; + nfiles++; + } + closedir(dir); + + if (dn == NULL) + return NULL; + + /* now that we know how many files there are + * allocate memory for an array to hold dnode pointers + */ + *nfiles_p = nfiles; + dnp = dnalloc(nfiles); + for (i = 0; /* i < nfiles - detected via !dn below */; i++) { + dnp[i] = dn; /* save pointer to node in array */ + dn = dn->dn_next; + if (!dn) + break; + } + + return dnp; +} + +#if ENABLE_DESKTOP +/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html + * If any of the -l, -n, -s options is specified, each list + * of files within the directory shall be preceded by a + * status line indicating the number of file system blocks + * occupied by files in the directory in 512-byte units if + * the -k option is not specified, or 1024-byte units if the + * -k option is specified, rounded up to the next integral + * number of units. + */ +/* by Jorgen Overgaard (jorgen AT antistaten.se) */ +static off_t calculate_blocks(struct dnode **dn) +{ + uoff_t blocks = 1; + if (dn) { + while (*dn) { + /* st_blocks is in 512 byte blocks */ + blocks += (*dn)->dn_blocks; + dn++; + } + } + + /* Even though standard says use 512 byte blocks, coreutils use 1k */ + /* Actually, we round up by calculating (blocks + 1) / 2, + * "+ 1" was done when we initialized blocks to 1 */ + return blocks >> 1; +} +#endif + +static void scan_and_display_dirs_recur(struct dnode **dn, int first) +{ + unsigned nfiles; + struct dnode **subdnp; + + for (; *dn; dn++) { + if (G.show_dirname || (option_mask32 & OPT_R)) { + if (!first) + bb_putchar('\n'); + first = 0; + printf("%s:\n", (*dn)->fullname); + } + subdnp = scan_one_dir((*dn)->fullname, &nfiles); +#if ENABLE_DESKTOP + if (option_mask32 & (OPT_s|OPT_l)) { + printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp)); + } +#endif + if (nfiles > 0) { + /* list all files at this level */ + sort_and_display_files(subdnp, nfiles); + + if (ENABLE_FEATURE_LS_RECURSIVE + && (option_mask32 & OPT_R) + ) { + struct dnode **dnd; + unsigned dndirs; + /* recursive - list the sub-dirs */ + dnd = splitdnarray(subdnp, SPLIT_SUBDIR); + dndirs = count_dirs(subdnp, SPLIT_SUBDIR); + if (dndirs > 0) { + dnsort(dnd, dndirs); + scan_and_display_dirs_recur(dnd, 0); + /* free the array of dnode pointers to the dirs */ + free(dnd); + } + } + /* free the dnodes and the fullname mem */ + dfree(subdnp); + } + } +} + + +int ls_main(int argc UNUSED_PARAM, char **argv) +{ /* ^^^^^^^^^^^^^^^^^ note: if FTPD, argc can be wrong, see ftpd.c */ + struct dnode **dnd; + struct dnode **dnf; + struct dnode **dnp; + struct dnode *dn; + struct dnode *cur; + unsigned opt; + unsigned nfiles; + unsigned dnfiles; + unsigned dndirs; + unsigned i; +#if ENABLE_FEATURE_LS_COLOR + /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ + /* coreutils 6.10: + * # ls --color=BOGUS + * ls: invalid argument 'BOGUS' for '--color' + * Valid arguments are: + * 'always', 'yes', 'force' + * 'never', 'no', 'none' + * 'auto', 'tty', 'if-tty' + * (and substrings: "--color=alwa" work too) + */ + static const char color_str[] ALIGN1 = + "always\0""yes\0""force\0" + "auto\0""tty\0""if-tty\0"; + /* need to initialize since --color has _an optional_ argument */ + const char *color_opt = color_str; /* "always" */ +#endif +#if ENABLE_LONG_OPTS + static const char ls_longopts[] ALIGN1 = + "full-time\0" No_argument "\xff" + "group-directories-first\0" No_argument "\xfe" + "color\0" Optional_argument "\xfd" + ; +#endif + + INIT_G(); + + init_unicode(); + +#if ENABLE_FEATURE_LS_WIDTH + /* obtain the terminal width */ + G_terminal_width = get_terminal_width(STDIN_FILENO); + /* go one less... */ + G_terminal_width--; +#endif + + /* process options */ + opt = getopt32long(argv, "^" + ls_options + "\0" + /* -n and -g imply -l */ + "nl:gl" + /* --full-time implies -l */ + IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l")) + /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html: + * in some pairs of opts, only last one takes effect: + */ + IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */ + // ":m-l:l-m" - we don't have -m + IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H") + ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */ + ":C-1:1-C" /* bycols/oneline */ + ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */ + IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */ + /* -w NUM: */ + IF_FEATURE_LS_WIDTH(":w+") + , ls_longopts + IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width) + IF_FEATURE_LS_COLOR(, &color_opt) + ); +#if 0 /* option bits debug */ + bb_error_msg("opt:0x%08x l:%x H:%x color:%x dirs:%x", opt, OPT_l, OPT_H, OPT_color, OPT_dirs_first); + if (opt & OPT_c ) bb_error_msg("-c"); + if (opt & OPT_l ) bb_error_msg("-l"); + if (opt & OPT_H ) bb_error_msg("-H"); + if (opt & OPT_color ) bb_error_msg("--color"); + if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first"); + if (opt & OPT_full_time ) bb_error_msg("--full-time"); + exit(0); +#endif + +#if ENABLE_SELINUX + if (opt & OPT_Z) { + if (!is_selinux_enabled()) + option_mask32 &= ~OPT_Z; + } +#endif + +#if ENABLE_FEATURE_LS_COLOR + /* set G_show_color = 1/0 */ + if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { + char *p = getenv("LS_COLORS"); + /* LS_COLORS is unset, or (not empty && not "none") ? */ + if (!p || (p[0] && strcmp(p, "none") != 0)) + G_show_color = 1; + } + if (opt & OPT_color) { + if (color_opt[0] == 'n') + G_show_color = 0; + else switch (index_in_substrings(color_str, color_opt)) { + case 3: + case 4: + case 5: + if (isatty(STDOUT_FILENO)) { + case 0: + case 1: + case 2: + G_show_color = 1; + } + } + } +#endif + + /* sort out which command line options take precedence */ + if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d)) + option_mask32 &= ~OPT_R; /* no recurse if listing only dir */ + if (!(opt & OPT_l)) { /* not -l? */ + if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { + /* when to sort by time? -t[cu] sorts by time even with -l */ + /* (this is achieved by opt_flags[] element for -t) */ + /* without -l, bare -c or -u enable sort too */ + /* (with -l, bare -c or -u just select which time to show) */ + if (opt & (OPT_c|OPT_u)) { + option_mask32 |= OPT_t; + } + } + } + + /* choose a display format if one was not already specified by an option */ + if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C))) + option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1); + + if (ENABLE_FTPD && applet_name[0] == 'f') { + /* ftpd secret backdoor. dirs first are much nicer */ + option_mask32 |= OPT_dirs_first; + } + + argv += optind; + if (!argv[0]) + *--argv = (char*)"."; + + if (argv[1]) + G.show_dirname = 1; /* 2 or more items? label directories */ + + /* stuff the command line file names into a dnode array */ + dn = NULL; + nfiles = 0; + do { + cur = my_stat(*argv, *argv, + /* follow links on command line unless -l, -s or -F: */ + !(option_mask32 & (OPT_l|OPT_s|OPT_F)) + /* ... or if -H: */ + || (option_mask32 & OPT_H) + /* ... or if -L, but my_stat always follows links if -L */ + ); + argv++; + if (!cur) + continue; + /*cur->fname_allocated = 0; - already is */ + cur->dn_next = dn; + dn = cur; + nfiles++; + } while (*argv); + + /* nfiles _may_ be 0 here - try "ls doesnt_exist" */ + if (nfiles == 0) + return G.exit_code; + + /* now that we know how many files there are + * allocate memory for an array to hold dnode pointers + */ + dnp = dnalloc(nfiles); + for (i = 0; /* i < nfiles - detected via !dn below */; i++) { + dnp[i] = dn; /* save pointer to node in array */ + dn = dn->dn_next; + if (!dn) + break; + } + + if (option_mask32 & OPT_d) { + sort_and_display_files(dnp, nfiles); + } else { + dnd = splitdnarray(dnp, SPLIT_DIR); + dnf = splitdnarray(dnp, SPLIT_FILE); + dndirs = count_dirs(dnp, SPLIT_DIR); + dnfiles = nfiles - dndirs; + if (dnfiles > 0) { + sort_and_display_files(dnf, dnfiles); + if (ENABLE_FEATURE_CLEAN_UP) + free(dnf); + } + if (dndirs > 0) { + dnsort(dnd, dndirs); + scan_and_display_dirs_recur(dnd, dnfiles == 0); + if (ENABLE_FEATURE_CLEAN_UP) + free(dnd); + } + } + + if (ENABLE_FEATURE_CLEAN_UP) + dfree(dnp); + return G.exit_code; +} diff --git a/busybox/coreutils/md5_sha1_sum.c b/busybox/coreutils/md5_sha1_sum.c new file mode 100644 index 00000000000000..87114514373390 --- /dev/null +++ b/busybox/coreutils/md5_sha1_sum.c @@ -0,0 +1,353 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 Glenn L. McGrath + * Copyright (C) 2003-2004 Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config MD5SUM +//config: bool "md5sum (6.8 kb)" +//config: default y +//config: help +//config: Compute and check MD5 message digest +//config: +//config:config SHA1SUM +//config: bool "sha1sum (6 kb)" +//config: default y +//config: help +//config: Compute and check SHA1 message digest +//config: +//config:config SHA256SUM +//config: bool "sha256sum (7.1 kb)" +//config: default y +//config: help +//config: Compute and check SHA256 message digest +//config: +//config:config SHA512SUM +//config: bool "sha512sum (7.6 kb)" +//config: default y +//config: help +//config: Compute and check SHA512 message digest +//config: +//config:config SHA3SUM +//config: bool "sha3sum (6.3 kb)" +//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: -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]..." +//usage:#define md5sum_full_usage "\n\n" +//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " MD5 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: ) +//usage: +//usage:#define md5sum_example_usage +//usage: "$ md5sum < busybox\n" +//usage: "6fd11e98b98a58f64ff3398d7b324003\n" +//usage: "$ md5sum busybox\n" +//usage: "6fd11e98b98a58f64ff3398d7b324003 busybox\n" +//usage: "$ md5sum -c -\n" +//usage: "6fd11e98b98a58f64ff3398d7b324003 busybox\n" +//usage: "busybox: OK\n" +//usage: "^D\n" +//usage: +//usage:#define sha1sum_trivial_usage +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." +//usage:#define sha1sum_full_usage "\n\n" +//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA1 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: ) +//usage: +//usage:#define sha256sum_trivial_usage +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." +//usage:#define sha256sum_full_usage "\n\n" +//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA256 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: ) +//usage: +//usage:#define sha512sum_trivial_usage +//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..." +//usage:#define sha512sum_full_usage "\n\n" +//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA512 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: ) +//usage: +//usage:#define sha3sum_trivial_usage +//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 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! */ + +enum { + /* 4th letter of applet_name is... */ + HASH_MD5 = 's', /* "md5>s= 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 */ + } + + { + RESERVE_CONFIG_UBUFFER(in_buf, 4096); + while ((count = safe_read(src_fd, in_buf, 4096)) > 0) { + update(&context, in_buf, count); + } + hash_value = NULL; + if (count < 0) + bb_perror_msg("can't read '%s'", filename); + else /* count == 0 */ { + final(&context, in_buf); + hash_value = hash_bin_to_hex(in_buf, hash_len); + } + RELEASE_CONFIG_BUFFER(in_buf); + } + + if (src_fd != STDIN_FILENO) { + close(src_fd); + } + + return hash_value; +} + +int md5_sha1_sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +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) { + /* -b "binary", -t "text" are ignored (shaNNNsum compat) */ + /* -s and -w require -c */ +#if ENABLE_SHA3SUM + if (applet_name[3] == HASH_SHA3) + flags = getopt32(argv, "^" "scwbta:+" "\0" "s?c:w?c", &sha3_width); + else +#endif + flags = getopt32(argv, "^" "scwbt" "\0" "s?c:w?c"); + } else { +#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*)"-"; + + do { + if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) { + FILE *pre_computed_stream; + char *line; + int count_total = 0; + int count_failed = 0; + + pre_computed_stream = xfopen_stdin(*argv); + + while ((line = xmalloc_fgetline(pre_computed_stream)) != NULL) { + uint8_t *hash_value; + char *filename_ptr; + + count_total++; + filename_ptr = strstr(line, " "); + /* handle format for binary checksums */ + if (filename_ptr == NULL) { + filename_ptr = strstr(line, " *"); + } + if (filename_ptr == NULL) { + if (flags & FLAG_WARN) { + bb_error_msg("invalid format"); + } + count_failed++; + return_value = EXIT_FAILURE; + free(line); + continue; + } + *filename_ptr = '\0'; + filename_ptr += 2; + + hash_value = hash_file(filename_ptr, sha3_width); + + if (hash_value && (strcmp((char*)hash_value, line) == 0)) { + if (!(flags & FLAG_SILENT)) + printf("%s: OK\n", filename_ptr); + } else { + if (!(flags & FLAG_SILENT)) + printf("%s: FAILED\n", filename_ptr); + count_failed++; + return_value = EXIT_FAILURE; + } + /* possible free(NULL) */ + free(hash_value); + free(line); + } + if (count_failed && !(flags & FLAG_SILENT)) { + 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: : 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, sha3_width); + if (hash_value == NULL) { + return_value = EXIT_FAILURE; + } else { + printf("%s %s\n", hash_value, *argv); + free(hash_value); + } + } + } while (*++argv); + + return return_value; +} diff --git a/busybox/coreutils/mkdir.c b/busybox/coreutils/mkdir.c new file mode 100644 index 00000000000000..986353dc6ef352 --- /dev/null +++ b/busybox/coreutils/mkdir.c @@ -0,0 +1,103 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mkdir implementation for busybox + * + * Copyright (C) 2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* 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 : Add SELinux Support. + */ +//config:config MKDIR +//config: bool "mkdir (4.4 kb)" +//config: default y +//config: help +//config: mkdir is used to create directories with the specified names. + +//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..." +//usage:#define mkdir_full_usage "\n\n" +//usage: "Create DIRECTORY\n" +//usage: "\n -m MODE Mode" +//usage: "\n -p No error if exists; make parent directories as needed" +//usage: IF_SELINUX( +//usage: "\n -Z Set security context" +//usage: ) +//usage: +//usage:#define mkdir_example_usage +//usage: "$ mkdir /tmp/foo\n" +//usage: "$ mkdir /tmp/foo\n" +//usage: "/tmp/foo: File exists\n" +//usage: "$ mkdir /tmp/foo/bar/baz\n" +//usage: "/tmp/foo/bar/baz: No such file or directory\n" +//usage: "$ mkdir -p /tmp/foo/bar/baz\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int mkdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mkdir_main(int argc UNUSED_PARAM, char **argv) +{ + long mode = -1; + int status = EXIT_SUCCESS; + int flags = 0; + unsigned opt; + char *smode; +#if ENABLE_SELINUX + security_context_t scontext; +#endif + + opt = getopt32long(argv, "m:pv" IF_SELINUX("Z:"), + "mode\0" Required_argument "m" + "parents\0" No_argument "p" +# if ENABLE_SELINUX + "context\0" Required_argument "Z" +# endif +# if ENABLE_FEATURE_VERBOSE + "verbose\0" No_argument "v" +# endif + , &smode IF_SELINUX(,&scontext) + ); + if (opt & 1) { + 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 & 8) { + selinux_or_die(); + setfscreatecon_or_die(scontext); + } +#endif + + argv += optind; + if (!argv[0]) + bb_show_usage(); + + do { + if (bb_make_directory(*argv, mode, flags)) { + status = EXIT_FAILURE; + } + } while (*++argv); + + return status; +} diff --git a/busybox/coreutils/mkfifo.c b/busybox/coreutils/mkfifo.c new file mode 100644 index 00000000000000..0f614017e5d857 --- /dev/null +++ b/busybox/coreutils/mkfifo.c @@ -0,0 +1,58 @@ +/* vi: set sw=4 ts=4: */ +/* + * mkfifo implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config MKFIFO +//config: bool "mkfifo (3.7 kb)" +//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 */ + +//usage:#define mkfifo_trivial_usage +//usage: "[-m MODE] " IF_SELINUX("[-Z] ") "NAME" +//usage:#define mkfifo_full_usage "\n\n" +//usage: "Create named pipe\n" +//usage: "\n -m MODE Mode (default a=rw)" +//usage: IF_SELINUX( +//usage: "\n -Z Set security context" +//usage: ) + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +/* This is a NOEXEC applet. Be very careful! */ + +int mkfifo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mkfifo_main(int argc UNUSED_PARAM, char **argv) +{ + mode_t mode; + int retval = EXIT_SUCCESS; + + mode = getopt_mk_fifo_nod(argv); + + argv += optind; + if (!*argv) { + bb_show_usage(); + } + + do { + if (mkfifo(*argv, mode) < 0) { + bb_simple_perror_msg(*argv); /* Avoid multibyte problems. */ + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} diff --git a/busybox/coreutils/mknod.c b/busybox/coreutils/mknod.c new file mode 100644 index 00000000000000..d57167f7d66b7e --- /dev/null +++ b/busybox/coreutils/mknod.c @@ -0,0 +1,86 @@ +/* vi: set sw=4 ts=4: */ +/* + * mknod implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config MKNOD +//config: bool "mknod (4 kb)" +//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. */ + +//usage:#define mknod_trivial_usage +//usage: "[-m MODE] " IF_SELINUX("[-Z] ") "NAME TYPE [MAJOR MINOR]" +//usage:#define mknod_full_usage "\n\n" +//usage: "Create a special file (block, character, or pipe)\n" +//usage: "\n -m MODE Creation mode (default a=rw)" +//usage: IF_SELINUX( +//usage: "\n -Z Set security context" +//usage: ) +//usage: "\nTYPE:" +//usage: "\n b Block device" +//usage: "\n c or u Character device" +//usage: "\n p Named pipe (MAJOR MINOR must be omitted)" +//usage: +//usage:#define mknod_example_usage +//usage: "$ mknod /dev/fd0 b 2 0\n" +//usage: "$ mknod -m 644 /tmp/pipe p\n" + +#include // For makedev + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +/* This is a NOEXEC applet. Be very careful! */ + +static const char modes_chars[] ALIGN1 = { 'p', 'c', 'u', 'b', 0, 1, 1, 2 }; +static const mode_t modes_cubp[] = { S_IFIFO, S_IFCHR, S_IFBLK }; + +int mknod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mknod_main(int argc UNUSED_PARAM, char **argv) +{ + mode_t mode; + dev_t dev; + const char *type, *arg; + + mode = getopt_mk_fifo_nod(argv); + argv += optind; + //argc -= optind; + + if (!argv[0] || !argv[1]) + bb_show_usage(); + type = strchr(modes_chars, argv[1][0]); + if (!type) + bb_show_usage(); + + mode |= modes_cubp[(int)(type[4])]; + + dev = 0; + arg = argv[2]; + if (*type != 'p') { + if (!argv[2] || !argv[3]) + bb_show_usage(); + /* Autodetect what the system supports; these macros should + * optimize out to two constants. */ + dev = makedev(xatoul_range(argv[2], 0, major(UINT_MAX)), + xatoul_range(argv[3], 0, minor(UINT_MAX))); + arg = argv[4]; + } + if (arg) + bb_show_usage(); + + if (mknod(argv[0], mode, dev) != 0) { + bb_simple_perror_msg_and_die(argv[0]); + } + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/mktemp.c b/busybox/coreutils/mktemp.c new file mode 100644 index 00000000000000..c041fedf7c678b --- /dev/null +++ b/busybox/coreutils/mktemp.c @@ -0,0 +1,119 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mktemp implementation for busybox + * + * Copyright (C) 2000 by Daniel Jacobowitz + * Written by Daniel Jacobowitz + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Coreutils 6.12 man page says: + * mktemp [OPTION]... [TEMPLATE] + * Create a temporary file or directory, safely, and print its name. If + * TEMPLATE is not specified, use tmp.XXXXXXXXXX. + * -d, --directory + * create a directory, not a file + * -q, --quiet + * suppress diagnostics about file/dir-creation failure + * -u, --dry-run + * do not create anything; merely print a name (unsafe) + * --tmpdir[=DIR] + * interpret TEMPLATE relative to DIR. If DIR is not specified, + * use $TMPDIR if set, else /tmp. With this option, TEMPLATE must + * not be an absolute name. Unlike with -t, TEMPLATE may contain + * slashes, but even here, mktemp still creates only the final com- + * ponent. + * -p DIR use DIR as a prefix; implies -t [deprecated] + * -t interpret TEMPLATE as a single file name component, relative to + * a directory: $TMPDIR, if set; else the directory specified via + * -p; else /tmp [deprecated] + */ +//config:config MKTEMP +//config: bool "mktemp (4 kb)" +//config: default y +//config: help +//config: mktemp is used to create unique temporary files + +//applet:IF_MKTEMP(APPLET_NOEXEC(mktemp, mktemp, BB_DIR_BIN, BB_SUID_DROP, mktemp)) + +//kbuild:lib-$(CONFIG_MKTEMP) += mktemp.o + +//usage:#define mktemp_trivial_usage +//usage: "[-dt] [-p DIR] [TEMPLATE]" +//usage:#define mktemp_full_usage "\n\n" +//usage: "Create a temporary file with name based on TEMPLATE and print its name.\n" +//usage: "TEMPLATE must end with XXXXXX (e.g. [/dir/]nameXXXXXX).\n" +//usage: "Without TEMPLATE, -t tmp.XXXXXX is assumed.\n" +//usage: "\n -d Make directory, not file" +//usage: "\n -q Fail silently on errors" +//usage: "\n -t Prepend base directory name to TEMPLATE" +//usage: "\n -p DIR Use DIR as a base directory (implies -t)" +//usage: "\n -u Do not create anything; print a name" +//usage: "\n" +//usage: "\nBase directory is: -p DIR, else $TMPDIR, else /tmp" +//usage: +//usage:#define mktemp_example_usage +//usage: "$ mktemp /tmp/temp.XXXXXX\n" +//usage: "/tmp/temp.mWiLjM\n" +//usage: "$ ls -la /tmp/temp.mWiLjM\n" +//usage: "-rw------- 1 andersen andersen 0 Apr 25 17:10 /tmp/temp.mWiLjM\n" + +#include "libbb.h" + +int mktemp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mktemp_main(int argc UNUSED_PARAM, char **argv) +{ + const char *path; + char *chp; + unsigned opts; + enum { + OPT_d = 1 << 0, + OPT_q = 1 << 1, + OPT_t = 1 << 2, + OPT_p = 1 << 3, + OPT_u = 1 << 4, + }; + + path = getenv("TMPDIR"); + if (!path || path[0] == '\0') + path = "/tmp"; + + opts = getopt32(argv, "^" "dqtp:u" "\0" "?1"/*1 arg max*/, &path); + + chp = argv[optind]; + if (!chp) { + /* GNU coreutils 8.4: + * bare "mktemp" -> "mktemp -t tmp.XXXXXX" + */ + chp = xstrdup("tmp.XXXXXX"); + opts |= OPT_t; + } +#if 0 + /* Don't allow directory separator in template */ + if ((opts & OPT_t) && bb_basename(chp) != chp) { + errno = EINVAL; + goto error; + } +#endif + if (opts & (OPT_t|OPT_p)) + chp = concat_path_file(path, chp); + + if (opts & OPT_u) { + chp = mktemp(chp); + if (chp[0] == '\0') + goto error; + } else if (opts & OPT_d) { + if (mkdtemp(chp) == NULL) + goto error; + } else { + if (mkstemp(chp) < 0) + goto error; + } + puts(chp); + return EXIT_SUCCESS; + error: + if (opts & OPT_q) + return EXIT_FAILURE; + /* don't use chp as it gets mangled in case of error */ + bb_perror_nomsg_and_die(); +} diff --git a/busybox/coreutils/mv.c b/busybox/coreutils/mv.c new file mode 100644 index 00000000000000..6e11197a1d35a9 --- /dev/null +++ b/busybox/coreutils/mv.c @@ -0,0 +1,165 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mv implementation for busybox + * + * Copyright (C) 2000 by Matt Kraai + * SELinux support by Yuichi Nakamura + * + * 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 (9.8 kb)" +//config: default y +//config: help +//config: mv is used to move or rename files or directories. + +//applet:IF_MV(APPLET_NOEXEC(mv, mv, BB_DIR_BIN, BB_SUID_DROP, mv)) +/* NOEXEC despite cases when it can be a "runner" (mv LARGE_DIR OTHER_FS) */ + +//kbuild:lib-$(CONFIG_MV) += mv.o + +//usage:#define mv_trivial_usage +//usage: "[-fin] SOURCE DEST\n" +//usage: "or: mv [-fin] SOURCE... DIRECTORY" +//usage:#define mv_full_usage "\n\n" +//usage: "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY\n" +//usage: "\n -f Don't prompt before overwriting" +//usage: "\n -i Interactive, prompt before overwrite" +//usage: "\n -n Don't overwrite an existing file" +//usage: +//usage:#define mv_example_usage +//usage: "$ mv /tmp/foo /bin/bar\n" + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mv_main(int argc, char **argv) +{ + struct stat dest_stat; + const char *last; + const char *dest; + unsigned flags; + int dest_exists; + int status = 0; + int copy_flag = 0; + +#define OPT_FORCE (1 << 0) +#define OPT_INTERACTIVE (1 << 1) +#define OPT_NOCLOBBER (1 << 2) +#define OPT_VERBOSE ((1 << 3) * ENABLE_FEATURE_VERBOSE) + /* 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). + */ + flags = getopt32long(argv, "^" + "finv" + "\0" + "-2:f-in:i-fn:n-fi", + "interactive\0" No_argument "i" + "force\0" No_argument "f" + "no-clobber\0" No_argument "n" + IF_FEATURE_VERBOSE( + "verbose\0" No_argument "v" + ) + ); + argc -= optind; + argv += optind; + last = argv[argc - 1]; + + if (argc == 2) { + dest_exists = cp_mv_stat(last, &dest_stat); + if (dest_exists < 0) { + return EXIT_FAILURE; + } + + if (!(dest_exists & 2)) { /* last is not a directory */ + dest = last; + goto DO_MOVE; + } + } + + do { + dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); + dest_exists = cp_mv_stat(dest, &dest_stat); + if (dest_exists < 0) { + goto RET_1; + } + + DO_MOVE: + if (dest_exists) { + if (flags & OPT_NOCLOBBER) + goto RET_0; + if (!(flags & OPT_FORCE) + && ((access(dest, W_OK) < 0 && isatty(0)) + || (flags & OPT_INTERACTIVE)) + ) { + if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { + goto RET_1; /* Ouch! fprintf failed! */ + } + if (!bb_ask_y_confirmation()) { + goto RET_0; + } + } + } + + if (rename(*argv, dest) < 0) { + struct stat source_stat; + int source_exists; + + if (errno != EXDEV + || (source_exists = cp_mv_stat2(*argv, &source_stat, lstat)) < 1 + ) { + bb_perror_msg("can't rename '%s'", *argv); + } else { + static const char fmt[] ALIGN1 = + "can't overwrite %sdirectory with %sdirectory"; + + if (dest_exists) { + if (dest_exists == 3) { + if (source_exists != 3) { + bb_error_msg(fmt, "", "non-"); + goto RET_1; + } + } else { + if (source_exists == 3) { + bb_error_msg(fmt, "non-", ""); + goto RET_1; + } + } + if (unlink(dest) < 0) { + bb_perror_msg("can't remove '%s'", dest); + goto RET_1; + } + } + /* FILEUTILS_RECUR also prevents nasties like + * "read from device and write contents to dst" + * instead of "create same device node" */ + copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS; +#if ENABLE_SELINUX + copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; +#endif + if ((copy_file(*argv, dest, copy_flag) >= 0) + && (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0) + ) { + goto RET_0; + } + } + RET_1: + status = 1; + } + RET_0: + if (flags & OPT_VERBOSE) { + printf("'%s' -> '%s'\n", *argv, dest); + } + if (dest != last) { + free((void *) dest); + } + } while (*++argv != last); + + return status; +} diff --git a/busybox/coreutils/nice.c b/busybox/coreutils/nice.c new file mode 100644 index 00000000000000..da1696c4201e41 --- /dev/null +++ b/busybox/coreutils/nice.c @@ -0,0 +1,68 @@ +/* vi: set sw=4 ts=4: */ +/* + * nice implementation for busybox + * + * Copyright (C) 2005 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config NICE +//config: bool "nice (1.8 kb)" +//config: default y +//config: help +//config: nice runs a program with modified scheduling priority. + +//applet:IF_NICE(APPLET_NOEXEC(nice, nice, BB_DIR_BIN, BB_SUID_DROP, nice)) + +//kbuild:lib-$(CONFIG_NICE) += nice.o + +//usage:#define nice_trivial_usage +//usage: "[-n ADJUST] [PROG ARGS]" +//usage:#define nice_full_usage "\n\n" +//usage: "Change scheduling priority, run PROG\n" +//usage: "\n -n ADJUST Adjust priority by ADJUST" + +#include "libbb.h" + +int nice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int nice_main(int argc UNUSED_PARAM, char **argv) +{ + int old_priority, adjustment; + + old_priority = getpriority(PRIO_PROCESS, 0); + + if (!*++argv) { /* No args, so (GNU) output current nice value. */ + printf("%d\n", old_priority); + fflush_stdout_and_exit(EXIT_SUCCESS); + } + + adjustment = 10; /* Set default adjustment. */ + + if (argv[0][0] == '-') { + char *nnn = argv[0] + 1; + if (nnn[0] == 'n') { /* -n */ + nnn += 1; + if (!nnn[0]) { /* "-n NNN" */ + nnn = *++argv; + } + /* else: "-nNNN" (w/o space) */ + } + /* else: "-NNN" (NNN may be negative) - same as "-n NNN" */ + + if (!nnn || !argv[1]) { /* Missing priority or PROG! */ + bb_show_usage(); + } + adjustment = xatoi_range(nnn, INT_MIN/2, INT_MAX/2); + argv++; + } + + { /* Set our priority. */ + int prio = old_priority + adjustment; + + if (setpriority(PRIO_PROCESS, 0, prio) < 0) { + bb_perror_msg_and_die("setpriority(%d)", prio); + } + } + + BB_EXECVP_or_die(argv); +} diff --git a/busybox/coreutils/nl.c b/busybox/coreutils/nl.c new file mode 100644 index 00000000000000..c2f8b10423be18 --- /dev/null +++ b/busybox/coreutils/nl.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2017 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config NL +//config: bool "nl (4.3 kb)" +//config: default y +//config: help +//config: nl is used to number lines of files. + +//applet:IF_NL(APPLET(nl, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_NL) += nl.o + +//usage:#define nl_trivial_usage +//usage: "[OPTIONS] [FILE]..." +//usage:#define nl_full_usage "\n\n" +//usage: "Write FILEs to standard output with line numbers added\n" +//usage: "\n -b STYLE Which lines to number - a: all, t: nonempty, n: none" +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^TODO: support "pBRE": number only lines thatmatch regexp BRE" +////usage: "\n -f STYLE footer lines" +////usage: "\n -h STYLE header lines" +////usage: "\n -d CC use CC for separating logical pages" +//usage: "\n -i N Line number increment" +////usage: "\n -l NUMBER group of NUMBER empty lines counted as one" +////usage: "\n -n FORMAT lneft justified, no leading zeros; rn or rz" +////usage: "\n -p do not reset line numbers at logical pages (huh?)" +//usage: "\n -s STRING Use STRING as line number separator" +//usage: "\n -v N Start from N" +//usage: "\n -w N Width of line numbers" + +/* By default, selects -v1 -i1 -l1 -sTAB -w6 -nrn -hn -bt -fn */ + +#include "libbb.h" + +int nl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int nl_main(int argc UNUSED_PARAM, char **argv) +{ + struct number_state ns; + const char *opt_b = "t"; + enum { + OPT_p = (1 << 0), + }; +#if ENABLE_LONG_OPTS + static const char nl_longopts[] ALIGN1 = + "body-numbering\0" Required_argument "b" + // "footer-numbering\0" Required_argument "f" - not implemented yet + // "header-numbering\0" Required_argument "h" - not implemented yet + // "section-delimiter\0" Required_argument "d" - not implemented yet + "line-increment\0" Required_argument "i" + // "join-blank-lines\0" Required_argument "l" - not implemented yet + // "number-format\0" Required_argument "n" - not implemented yet + "no-renumber\0" No_argument "p" // no-op so far + "number-separator\0" Required_argument "s" + "starting-line-number\0"Required_argument "v" + "number-width\0" Required_argument "w" + ; +#endif + ns.width = 6; + ns.start = 1; + ns.inc = 1; + ns.sep = "\t"; + getopt32long(argv, "pw:+s:v:+i:+b:", nl_longopts, + &ns.width, &ns.sep, &ns.start, &ns.inc, &opt_b); + ns.all = (opt_b[0] == 'a'); + ns.nonempty = (opt_b[0] == 't'); + ns.empty_str = xasprintf("%*s\n", ns.width + (int)strlen(ns.sep), ""); + + argv += optind; + if (!*argv) + *--argv = (char*)"-"; + + do { + print_numbered_lines(&ns, *argv); + } while (*++argv); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/nohup.c b/busybox/coreutils/nohup.c new file mode 100644 index 00000000000000..ae136e085743e8 --- /dev/null +++ b/busybox/coreutils/nohup.c @@ -0,0 +1,98 @@ +/* vi: set sw=4 ts=4: */ +/* + * nohup - invoke a utility immune to hangups. + * + * Busybox version based on nohup specification at + * http://www.opengroup.org/onlinepubs/007904975/utilities/nohup.html + * + * Copyright 2006 Rob Landley + * Copyright 2006 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config NOHUP +//config: bool "nohup (2 kb)" +//config: default y +//config: help +//config: run a command immune to hangups, with output to a non-tty. + +//applet:IF_NOHUP(APPLET_NOEXEC(nohup, nohup, BB_DIR_USR_BIN, BB_SUID_DROP, nohup)) + +//kbuild:lib-$(CONFIG_NOHUP) += nohup.o + +//usage:#define nohup_trivial_usage +//usage: "PROG ARGS" +//usage:#define nohup_full_usage "\n\n" +//usage: "Run PROG immune to hangups, with output to a non-tty" +//usage: +//usage:#define nohup_example_usage +//usage: "$ nohup make &" + +#include "libbb.h" + +/* Compat info: nohup (GNU coreutils 6.8) does this: +# nohup true +nohup: ignoring input and appending output to 'nohup.out' +# nohup true 1>/dev/null +nohup: ignoring input and redirecting stderr to stdout +# nohup true 2>zz +# cat zz +nohup: ignoring input and appending output to 'nohup.out' +# nohup true 2>zz 1>/dev/null +# cat zz +nohup: ignoring input +# nohup true /dev/null +nohup: redirecting stderr to stdout +# nohup true zz 1>/dev/null +# cat zz + (nothing) +# +*/ + +int nohup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int nohup_main(int argc UNUSED_PARAM, char **argv) +{ + const char *nohupout; + char *home; + + xfunc_error_retval = 127; + + if (!argv[1]) { + bb_show_usage(); + } + + /* If stdin is a tty, detach from it. */ + if (isatty(STDIN_FILENO)) { + /* bb_error_msg("ignoring input"); */ + close(STDIN_FILENO); + xopen(bb_dev_null, O_RDONLY); /* will be fd 0 (STDIN_FILENO) */ + } + + nohupout = "nohup.out"; + /* Redirect stdout to nohup.out, either in "." or in "$HOME". */ + if (isatty(STDOUT_FILENO)) { + close(STDOUT_FILENO); + if (open(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR) < 0) { + home = getenv("HOME"); + if (home) { + nohupout = concat_path_file(home, nohupout); + xopen3(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR); + } else { + xopen(bb_dev_null, O_RDONLY); /* will be fd 1 */ + } + } + bb_error_msg("appending output to %s", nohupout); + } + + /* If we have a tty on stderr, redirect to stdout. */ + if (isatty(STDERR_FILENO)) { + /* if (stdout_wasnt_a_tty) + bb_error_msg("redirecting stderr to stdout"); */ + dup2(STDOUT_FILENO, STDERR_FILENO); + } + + signal(SIGHUP, SIG_IGN); + + argv++; + BB_EXECVP_or_die(argv); +} diff --git a/busybox/coreutils/nproc.c b/busybox/coreutils/nproc.c new file mode 100644 index 00000000000000..0ea8d100127917 --- /dev/null +++ b/busybox/coreutils/nproc.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2017 Denys Vlasenko + * + * Licensed under GPLv2, see LICENSE in this source tree + */ +//config:config NPROC +//config: bool "nproc (248 bytes)" +//config: default y +//config: help +//config: Print number of CPUs + +//applet:IF_NPROC(APPLET_NOFORK(nproc, nproc, BB_DIR_USR_BIN, BB_SUID_DROP, nproc)) + +//kbuild:lib-$(CONFIG_NPROC) += nproc.o + +//usage:#define nproc_trivial_usage +//usage: ""IF_LONG_OPTS("--all --ignore=N") +//usage:#define nproc_full_usage "\n\n" +//usage: "Print number of available CPUs" +//usage: IF_LONG_OPTS( +//usage: "\n" +//usage: "\n --all Number of installed CPUs" +//usage: "\n --ignore=N Exclude N CPUs" +//usage: ) + +#include +#include "libbb.h" + +int nproc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + unsigned long mask[1024]; + int count = 0; +#if ENABLE_LONG_OPTS + int ignore = 0; + int opts = getopt32long(argv, "\xfe:+", + "ignore\0" Required_argument "\xfe" + "all\0" No_argument "\xff" + , &ignore + ); + + if (opts & (1 << 1)) { + DIR *cpusd = opendir("/sys/devices/system/cpu"); + if (cpusd) { + struct dirent *de; + while (NULL != (de = readdir(cpusd))) { + char *cpuid = strstr(de->d_name, "cpu"); + if (cpuid && isdigit(cpuid[strlen(cpuid) - 1])) + count++; + } + closedir(cpusd); + } + } else +#endif + if (sched_getaffinity(0, sizeof(mask), (void*)mask) == 0) { + int i; + for (i = 0; i < ARRAY_SIZE(mask); i++) { + unsigned long m = mask[i]; + while (m) { + if (m & 1) + count++; + m >>= 1; + } + } + } + + IF_LONG_OPTS(count -= ignore;) + if (count <= 0) + count = 1; + + printf("%u\n", count); + + return 0; +} diff --git a/busybox/coreutils/od.c b/busybox/coreutils/od.c new file mode 100644 index 00000000000000..9a888dd5f53b84 --- /dev/null +++ b/busybox/coreutils/od.c @@ -0,0 +1,237 @@ +/* vi: set sw=4 ts=4: */ +/* + * od implementation for busybox + * Based on code from util-linux v 2.11l + * + * Copyright (c) 1990 + * The Regents of the University of California. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Original copyright notice is retained at the end of this file. + */ +//config:config OD +//config: bool "od (11 kb)" +//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 +//usage: "[-aBbcDdeFfHhIiLlOovXx] [FILE]" +//usage:#define od_full_usage "\n\n" +//usage: "Print FILE (or stdin) unambiguously, as octal bytes by default" +//usage:#endif + +#include "libbb.h" +#if ENABLE_DESKTOP +/* This one provides -t (busybox's own build script needs it) */ +#include "od_bloaty.c" +#else + +#include "dump.h" + +static void +odoffset(dumper_t *dumper, int argc, char ***argvp) +{ + char *num, *p; + int base; + char *end; + + /* + * The offset syntax of od(1) was genuinely bizarre. First, if + * it started with a plus it had to be an offset. Otherwise, if + * there were at least two arguments, a number or lower-case 'x' + * followed by a number makes it an offset. By default it was + * octal; if it started with 'x' or '0x' it was hex. If it ended + * in a '.', it was decimal. If a 'b' or 'B' was appended, it + * multiplied the number by 512 or 1024 byte units. There was + * no way to assign a block count to a hex offset. + * + * We assumes it's a file if the offset is bad. + */ + p = **argvp; + + if (!p) { + /* hey someone is probably piping to us ... */ + return; + } + + if ((*p != '+') + && (argc < 2 + || (!isdigit(p[0]) + && ((p[0] != 'x') || !isxdigit(p[1]))))) + return; + + base = 0; + /* + * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and + * set base. + */ + if (p[0] == '+') + ++p; + if (p[0] == 'x' && isxdigit(p[1])) { + ++p; + base = 16; + } else if (p[0] == '0' && p[1] == 'x') { + p += 2; + base = 16; + } + + /* skip over the number */ + if (base == 16) + for (num = p; isxdigit(*p); ++p) + continue; + else + for (num = p; isdigit(*p); ++p) + continue; + + /* check for no number */ + if (num == p) + return; + + /* if terminates with a '.', base is decimal */ + if (*p == '.') { + if (base) + return; + base = 10; + } + + dumper->dump_skip = strtol(num, &end, base ? base : 8); + + /* if end isn't the same as p, we got a non-octal digit */ + if (end != p) + dumper->dump_skip = 0; + else { + if (*p) { + if (*p == 'b') { + dumper->dump_skip *= 512; + ++p; + } else if (*p == 'B') { + dumper->dump_skip *= 1024; + ++p; + } + } + if (*p) + dumper->dump_skip = 0; + else { + ++*argvp; + /* + * If the offset uses a non-octal base, the base of + * the offset is changed as well. This isn't pretty, + * but it's easy. + */ +#define TYPE_OFFSET 7 + { + char x_or_d; + if (base == 16) { + x_or_d = 'x'; + goto DO_X_OR_D; + } + if (base == 10) { + x_or_d = 'd'; + DO_X_OR_D: + dumper->fshead->nextfu->fmt[TYPE_OFFSET] + = dumper->fshead->nextfs->nextfu->fmt[TYPE_OFFSET] + = x_or_d; + } + } + } + } +} + +static const char *const add_strings[] = { + "16/1 \"%3_u \" \"\\n\"", /* a */ + "8/2 \" %06o \" \"\\n\"", /* B, o */ + "16/1 \"%03o \" \"\\n\"", /* b */ + "16/1 \"%3_c \" \"\\n\"", /* c */ + "8/2 \" %05u \" \"\\n\"", /* d */ + "4/4 \" %010u \" \"\\n\"", /* D */ + "2/8 \" %21.14e \" \"\\n\"", /* e (undocumented in od), F */ + "4/4 \" %14.7e \" \"\\n\"", /* f */ + "4/4 \" %08x \" \"\\n\"", /* H, X */ + "8/2 \" %04x \" \"\\n\"", /* h, x */ + "4/4 \" %11d \" \"\\n\"", /* I, L, l */ + "8/2 \" %6d \" \"\\n\"", /* i */ + "4/4 \" %011o \" \"\\n\"", /* O */ +}; + +static const char od_opts[] ALIGN1 = "aBbcDdeFfHhIiLlOoXxv"; + +static const char od_o2si[] ALIGN1 = { + 0, 1, 2, 3, 5, + 4, 6, 6, 7, 8, + 9, 0xa, 0xb, 0xa, 0xa, + 0xb, 1, 8, 9, +}; + +int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int od_main(int argc, char **argv) +{ + int ch; + int first = 1; + char *p; + dumper_t *dumper = alloc_dumper(); + + while ((ch = getopt(argc, argv, od_opts)) > 0) { + if (ch == 'v') { + dumper->dump_vflag = ALL; + } else if (((p = strchr(od_opts, ch)) != NULL) && (*p != '\0')) { + if (first) { + first = 0; + bb_dump_add(dumper, "\"%07.7_Ao\n\""); + bb_dump_add(dumper, "\"%07.7_ao \""); + } else { + bb_dump_add(dumper, "\" \""); + } + bb_dump_add(dumper, add_strings[(int)od_o2si[(p - od_opts)]]); + } else { /* P, p, s, w, or other unhandled */ + bb_show_usage(); + } + } + if (!dumper->fshead) { + bb_dump_add(dumper, "\"%07.7_Ao\n\""); + bb_dump_add(dumper, "\"%07.7_ao \" 8/2 \"%06o \" \"\\n\""); + } + + argc -= optind; + argv += optind; + + odoffset(dumper, argc, &argv); + + return bb_dump_dump(dumper, argv); +} +#endif /* ENABLE_DESKTOP */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/busybox/coreutils/od_bloaty.c b/busybox/coreutils/od_bloaty.c new file mode 100644 index 00000000000000..4cae0c5299321f --- /dev/null +++ b/busybox/coreutils/od_bloaty.c @@ -0,0 +1,1395 @@ +/* od -- dump files in octal and other formats + Copyright (C) 92, 1995-2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* Written by Jim Meyering. */ +/* Busyboxed by Denys Vlasenko, based on od.c from coreutils-5.2.1 */ + + +/* #include "libbb.h" - done in od.c */ +#include "common_bufsiz.h" +#define assert(a) ((void)0) + + +//usage:#if ENABLE_DESKTOP +//usage:#define od_trivial_usage +//usage: "[-abcdfhilovxs] [-t TYPE] [-A RADIX] [-N SIZE] [-j SKIP] [-S MINSTR] [-w WIDTH] [FILE]..." +// We don't support: +// ... [FILE] [[+]OFFSET[.][b]] +// Support is buggy for: +// od --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]] + +//usage:#define od_full_usage "\n\n" +//usage: "Print FILEs (or stdin) unambiguously, as octal bytes by default" +//usage:#endif + +enum { + OPT_A = 1 << 0, + OPT_N = 1 << 1, + OPT_a = 1 << 2, + OPT_b = 1 << 3, + OPT_c = 1 << 4, + OPT_d = 1 << 5, + OPT_f = 1 << 6, + OPT_h = 1 << 7, + OPT_i = 1 << 8, + OPT_j = 1 << 9, + OPT_l = 1 << 10, + OPT_o = 1 << 11, + OPT_t = 1 << 12, + /* When zero and two or more consecutive blocks are equal, format + only the first block and output an asterisk alone on the following + line to indicate that identical blocks have been elided: */ + OPT_v = 1 << 13, + OPT_x = 1 << 14, + OPT_s = 1 << 15, + OPT_S = 1 << 16, + OPT_w = 1 << 17, + OPT_traditional = (1 << 18) * ENABLE_LONG_OPTS, +}; + +#define OD_GETOPT32() getopt32long(argv, \ + "A:N:abcdfhij:lot:*vxsS:w:+:", od_longopts, \ + /* -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, &G.bytes_per_block) + + +/* Check for 0x7f is a coreutils 6.3 addition */ +#define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f) + +typedef long double longdouble_t; +typedef unsigned long long ulonglong_t; +typedef long long llong; + +#if ENABLE_LFS +# define xstrtooff_sfx xstrtoull_sfx +#else +# define xstrtooff_sfx xstrtoul_sfx +#endif + +/* The default number of input bytes per output line. */ +#define DEFAULT_BYTES_PER_BLOCK 16 + +/* The number of decimal digits of precision in a float. */ +#ifndef FLT_DIG +# define FLT_DIG 7 +#endif + +/* The number of decimal digits of precision in a double. */ +#ifndef DBL_DIG +# define DBL_DIG 15 +#endif + +/* The number of decimal digits of precision in a long double. */ +#ifndef LDBL_DIG +# define LDBL_DIG DBL_DIG +#endif + +enum size_spec { + NO_SIZE, + CHAR, + SHORT, + INT, + LONG, + LONG_LONG, + FLOAT_SINGLE, + FLOAT_DOUBLE, + FLOAT_LONG_DOUBLE, + N_SIZE_SPECS +}; + +enum output_format { + SIGNED_DECIMAL, + UNSIGNED_DECIMAL, + OCTAL, + HEXADECIMAL, + FLOATING_POINT, + NAMED_CHARACTER, + CHARACTER +}; + +/* Each output format specification (from '-t spec' or from + old-style options) is represented by one of these structures. */ +struct tspec { + enum output_format fmt; + enum size_spec size; + void (*print_function) (size_t, const char *, const char *); + char *fmt_string; + int hexl_mode_trailer; + int field_width; +}; + +/* Convert the number of 8-bit bytes of a binary representation to + the number of characters (digits + sign if the type is signed) + required to represent the same quantity in the specified base/type. + For example, a 32-bit (4-byte) quantity may require a field width + as wide as the following for these types: + 11 unsigned octal + 11 signed decimal + 10 unsigned decimal + 8 unsigned hexadecimal */ + +static const uint8_t bytes_to_oct_digits[] ALIGN1 = +{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43}; + +static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 = +{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40}; + +static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 = +{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39}; + +static const uint8_t bytes_to_hex_digits[] ALIGN1 = +{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32}; + +/* Convert enum size_spec to the size of the named type. */ +static const signed char width_bytes[] ALIGN1 = { + -1, + sizeof(char), + sizeof(short), + sizeof(int), + sizeof(long), + sizeof(ulonglong_t), + sizeof(float), + sizeof(double), + sizeof(longdouble_t) +}; +/* Ensure that for each member of 'enum size_spec' there is an + initializer in the width_bytes array. */ +struct ERR_width_bytes_has_bad_size { + char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; +}; + +struct globals { + smallint exit_code; + + unsigned string_min; + + /* 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. */ + 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 + 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. */ + 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; + + char address_fmt[sizeof("%0n"OFF_FMT"xc")]; +} FIX_ALIASING; +/* Corresponds to 'x' above */ +#define address_base_char G.address_fmt[sizeof(G.address_fmt)-3] +/* Corresponds to 'n' above */ +#define address_pad_len_char G.address_fmt[2] + +#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; \ + strcpy(G.address_fmt, "%0n"OFF_FMT"xc"); \ +} while (0) + + +#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t) +static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = { + [sizeof(char)] = CHAR, +#if USHRT_MAX != UCHAR_MAX + [sizeof(short)] = SHORT, +#endif +#if UINT_MAX != USHRT_MAX + [sizeof(int)] = INT, +#endif +#if ULONG_MAX != UINT_MAX + [sizeof(long)] = LONG, +#endif +#if ULLONG_MAX != ULONG_MAX + [sizeof(ulonglong_t)] = LONG_LONG, +#endif +}; + +#define MAX_FP_TYPE_SIZE sizeof(longdouble_t) +static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { + /* gcc seems to allow repeated indexes. Last one wins */ + [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, + [sizeof(double)] = FLOAT_DOUBLE, + [sizeof(float)] = FLOAT_SINGLE +}; + + +static unsigned +gcd(unsigned u, unsigned v) +{ + unsigned t; + while (v != 0) { + t = u % v; + u = v; + v = t; + } + return u; +} + +/* Compute the least common multiple of U and V. */ +static unsigned +lcm(unsigned u, unsigned v) { + unsigned t = gcd(u, v); + if (t == 0) + return 0; + return u * v / t; +} + +static void +print_s_char(size_t n_bytes, const char *block, const char *fmt_string) +{ + while (n_bytes--) { + int tmp = *(signed char *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned char); + } +} + +static void +print_char(size_t n_bytes, const char *block, const char *fmt_string) +{ + while (n_bytes--) { + unsigned tmp = *(unsigned char *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned char); + } +} + +static void +print_s_short(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(signed short); + while (n_bytes--) { + int tmp = *(signed short *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned short); + } +} + +static void +print_short(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(unsigned short); + while (n_bytes--) { + unsigned tmp = *(unsigned short *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned short); + } +} + +static void +print_int(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(unsigned); + while (n_bytes--) { + unsigned tmp = *(unsigned *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned); + } +} + +#if UINT_MAX == ULONG_MAX +# define print_long print_int +#else +static void +print_long(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(unsigned long); + while (n_bytes--) { + unsigned long tmp = *(unsigned long *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned long); + } +} +#endif + +#if ULONG_MAX == ULLONG_MAX +# define print_long_long print_long +#else +static void +print_long_long(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(ulonglong_t); + while (n_bytes--) { + ulonglong_t tmp = *(ulonglong_t *) block; + printf(fmt_string, tmp); + block += sizeof(ulonglong_t); + } +} +#endif + +static void +print_float(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(float); + while (n_bytes--) { + float tmp = *(float *) block; + printf(fmt_string, tmp); + block += sizeof(float); + } +} + +static void +print_double(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(double); + while (n_bytes--) { + double tmp = *(double *) block; + printf(fmt_string, tmp); + block += sizeof(double); + } +} + +static void +print_long_double(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(longdouble_t); + while (n_bytes--) { + longdouble_t tmp = *(longdouble_t *) block; + printf(fmt_string, tmp); + block += sizeof(longdouble_t); + } +} + +/* print_[named]_ascii are optimized for speed. + * Remember, someday you may want to pump gigabytes through this thing. + * Saving a dozen of .text bytes here is counter-productive */ + +static void +print_named_ascii(size_t n_bytes, const char *block, + const char *unused_fmt_string UNUSED_PARAM) +{ + /* Names for some non-printing characters. */ + static const char charname[33][3] ALIGN1 = { + "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", + " bs", " ht", " nl", " vt", " ff", " cr", " so", " si", + "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", + "can", " em", "sub", "esc", " fs", " gs", " rs", " us", + " sp" + }; + // buf[N] pos: 01234 56789 + 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++; + + masked_c &= 0x7f; + if (masked_c == 0x7f) { + fputs(" del", stdout); + continue; + } + if (masked_c > ' ') { + buf[3] = masked_c; + fputs(buf, stdout); + continue; + } + /* Why? Because printf(" %3.3s") is much slower... */ + buf[6] = charname[masked_c][0]; + buf[7] = charname[masked_c][1]; + buf[8] = charname[masked_c][2]; + fputs(buf+5, stdout); + } +} + +static void +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 xxx\0"; + + while (n_bytes--) { + const char *s; + unsigned c = *(unsigned char *) block++; + + if (ISPRINT(c)) { + buf[3] = c; + fputs(buf, stdout); + continue; + } + switch (c) { + case '\0': + s = " \\0"; + break; + case '\007': + s = " \\a"; + break; + case '\b': + s = " \\b"; + break; + case '\f': + s = " \\f"; + break; + case '\n': + s = " \\n"; + break; + case '\r': + s = " \\r"; + break; + case '\t': + s = " \\t"; + break; + case '\v': + s = " \\v"; + break; + default: + buf[6] = (c >> 6 & 3) + '0'; + buf[7] = (c >> 3 & 7) + '0'; + buf[8] = (c & 7) + '0'; + s = buf + 5; + } + fputs(s, stdout); + } +} + +/* Given a list of one or more input filenames FILE_LIST, set the global + file pointer IN_STREAM and the global string INPUT_FILENAME to the + first one that can be successfully opened. Modify FILE_LIST to + reference the next filename in the list. A file name of "-" is + interpreted as standard input. If any file open fails, give an error + message and return nonzero. */ + +static void +open_next_file(void) +{ + while (1) { + if (!*G.file_list) + return; + G.in_stream = fopen_or_warn_stdin(*G.file_list++); + if (G.in_stream) { + break; + } + G.exit_code = 1; + } + + if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N) + setbuf(G.in_stream, NULL); +} + +/* Test whether there have been errors on in_stream, and close it if + it is not standard input. Return nonzero if there has been an error + on in_stream or stdout; return zero otherwise. This function will + report more than one error only if both a read and a write error + have occurred. IN_ERRNO, if nonzero, is the error number + corresponding to the most recent action for IN_STREAM. */ + +static void +check_and_close(void) +{ + if (G.in_stream) { + if (ferror(G.in_stream)) { + bb_error_msg("%s: read error", (G.in_stream == stdin) + ? bb_msg_standard_input + : G.file_list[-1] + ); + G.exit_code = 1; + } + fclose_if_not_stdin(G.in_stream); + G.in_stream = NULL; + } + + if (ferror(stdout)) { + bb_error_msg_and_die(bb_msg_write_error); + } +} + +/* If S points to a single valid modern od format string, put + a description of that format in *TSPEC, return pointer to + character following the just-decoded format. + For example, if S were "d4afL", we will return a rtp to "afL" + and *TSPEC would be + { + fmt = SIGNED_DECIMAL; + size = INT or LONG; (whichever integral_type_size[4] resolves to) + print_function = print_int; (assuming size == INT) + fmt_string = "%011d%c"; + } + S_ORIG is solely for reporting errors. It should be the full format + string argument. */ + +static NOINLINE const char * +decode_one_format(const char *s_orig, const char *s, struct tspec *tspec) +{ + enum size_spec size_spec; + unsigned size; + enum output_format fmt; + const char *p; + char *end; + char *fmt_string = NULL; + void (*print_function) (size_t, const char *, const char *); + unsigned c; + unsigned field_width = 0; + int pos; + + switch (*s) { + case 'd': + case 'o': + case 'u': + case 'x': { + static const char CSIL[] ALIGN1 = "CSIL"; + + c = *s++; + p = strchr(CSIL, *s); + /* if *s == NUL, p != NULL! Testcase: "od -tx" */ + if (!p || *p == '\0') { + size = sizeof(int); + if (isdigit(s[0])) { + size = bb_strtou(s, &end, 0); + if (errno == ERANGE + || MAX_INTEGRAL_TYPE_SIZE < size + || integral_type_size[size] == NO_SIZE + ) { + bb_error_msg_and_die("invalid type string '%s'; " + "%u-byte %s type is not supported", + s_orig, size, "integral"); + } + s = end; + } + } else { + static const uint8_t CSIL_sizeof[4] = { + sizeof(char), + sizeof(short), + sizeof(int), + sizeof(long), + }; + size = CSIL_sizeof[p - CSIL]; + s++; /* skip C/S/I/L */ + } + +#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \ + ((Spec) == LONG_LONG ? (Max_format) \ + : ((Spec) == LONG ? (Long_format) : (Min_format))) + +#define FMT_BYTES_ALLOCATED 9 + size_spec = integral_type_size[size]; + + { + static const char doux[] ALIGN1 = "doux"; + static const char doux_fmt_letter[][4] = { + "lld", "llo", "llu", "llx" + }; + static const enum output_format doux_fmt[] = { + SIGNED_DECIMAL, + OCTAL, + UNSIGNED_DECIMAL, + HEXADECIMAL, + }; + static const uint8_t *const doux_bytes_to_XXX[] = { + bytes_to_signed_dec_digits, + bytes_to_oct_digits, + bytes_to_unsigned_dec_digits, + bytes_to_hex_digits, + }; + static const char doux_fmtstring[][sizeof(" %%0%u%s")] = { + " %%%u%s", + " %%0%u%s", + " %%%u%s", + " %%0%u%s", + }; + + pos = strchr(doux, c) - doux; + fmt = doux_fmt[pos]; + field_width = doux_bytes_to_XXX[pos][size]; + p = doux_fmt_letter[pos] + 2; + if (size_spec == LONG) p--; + if (size_spec == LONG_LONG) p -= 2; + fmt_string = xasprintf(doux_fmtstring[pos], field_width, p); + } + + switch (size_spec) { + case CHAR: + print_function = (fmt == SIGNED_DECIMAL + ? print_s_char + : print_char); + break; + case SHORT: + print_function = (fmt == SIGNED_DECIMAL + ? print_s_short + : print_short); + break; + case INT: + print_function = print_int; + break; + case LONG: + print_function = print_long; + break; + default: /* case LONG_LONG: */ + print_function = print_long_long; + break; + } + break; + } + + case 'f': { + static const char FDL[] ALIGN1 = "FDL"; + + fmt = FLOATING_POINT; + ++s; + p = strchr(FDL, *s); + if (!p || *p == '\0') { + size = sizeof(double); + if (isdigit(s[0])) { + size = bb_strtou(s, &end, 0); + if (errno == ERANGE || size > MAX_FP_TYPE_SIZE + || fp_type_size[size] == NO_SIZE + ) { + bb_error_msg_and_die("invalid type string '%s'; " + "%u-byte %s type is not supported", + s_orig, size, "floating point"); + } + s = end; + } + } else { + static const uint8_t FDL_sizeof[] = { + sizeof(float), + sizeof(double), + sizeof(longdouble_t), + }; + + size = FDL_sizeof[p - FDL]; + s++; /* skip F/D/L */ + } + + size_spec = fp_type_size[size]; + + switch (size_spec) { + case FLOAT_SINGLE: + print_function = print_float; + field_width = FLT_DIG + 8; + /* Don't use %#e; not all systems support it. */ + fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG); + break; + case FLOAT_DOUBLE: + print_function = print_double; + field_width = DBL_DIG + 8; + fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG); + break; + default: /* case FLOAT_LONG_DOUBLE: */ + print_function = print_long_double; + field_width = LDBL_DIG + 8; + fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG); + break; + } + break; + } + + case 'a': + ++s; + fmt = NAMED_CHARACTER; + size_spec = CHAR; + print_function = print_named_ascii; + field_width = 3; + break; + case 'c': + ++s; + fmt = CHARACTER; + size_spec = CHAR; + print_function = print_ascii; + field_width = 3; + break; + default: + bb_error_msg_and_die("invalid character '%c' " + "in type string '%s'", *s, s_orig); + } + + tspec->size = size_spec; + tspec->fmt = fmt; + tspec->print_function = print_function; + tspec->fmt_string = fmt_string; + + tspec->field_width = field_width; + tspec->hexl_mode_trailer = (*s == 'z'); + if (tspec->hexl_mode_trailer) + s++; + + return s; +} + +/* Decode the modern od format string S. Append the decoded + representation to the global array SPEC, reallocating SPEC if + necessary. */ + +static void +decode_format_string(const char *s) +{ + const char *s_orig = s; + + while (*s != '\0') { + struct tspec tspec; + const char *next; + + next = decode_one_format(s_orig, s, &tspec); + + assert(s != next); + s = next; + G.spec = xrealloc_vector(G.spec, 4, G.n_specs); + memcpy(&G.spec[G.n_specs], &tspec, sizeof(G.spec[0])); + G.n_specs++; + } +} + +/* Given a list of one or more input filenames FILE_LIST, set the global + file pointer IN_STREAM to position N_SKIP in the concatenation of + those files. If any file operation fails or if there are fewer than + N_SKIP bytes in the combined input, give an error message and return + nonzero. When possible, use seek rather than read operations to + advance IN_STREAM. */ + +static void +skip(off_t n_skip) +{ + if (n_skip == 0) + return; + + while (G.in_stream) { /* !EOF */ + struct stat file_stats; + + /* First try seeking. For large offsets, this extra work is + worthwhile. If the offset is below some threshold it may be + more efficient to move the pointer by reading. There are two + issues when trying to seek: + - the file must be seekable. + - before seeking to the specified position, make sure + that the new position is in the current file. + Try to do that by getting file's size using fstat. + But that will work only for regular files. */ + + /* The st_size field is valid only for regular files + (and for symbolic links, which cannot occur here). + 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(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(G.in_stream, n_skip, SEEK_CUR) != 0) + G.exit_code = 1; + return; + } + } else { + /* If it's not a regular file with positive size, + position the file pointer by reading. */ + char buf[1024]; + size_t n_bytes_to_read = 1024; + size_t n_bytes_read; + + 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, G.in_stream); + n_skip -= n_bytes_read; + if (n_bytes_read != n_bytes_to_read) + break; /* EOF on this file or error */ + } + } + if (n_skip == 0) + return; + + check_and_close(); + open_next_file(); + } + + if (n_skip) + bb_error_msg_and_die("can't skip past end of combined input"); +} + + +typedef void FN_format_address(off_t address, char c); + +static void +format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM) +{ +} + +static void +format_address_std(off_t address, char c) +{ + /* Corresponds to 'c' */ + G.address_fmt[sizeof(G.address_fmt)-2] = c; + printf(G.address_fmt, address); +} + +#if ENABLE_LONG_OPTS +/* only used with --traditional */ +static void +format_address_paren(off_t address, char c) +{ + putchar('('); + format_address_std(address, ')'); + if (c) putchar(c); +} + +static void +format_address_label(off_t address, char c) +{ + format_address_std(address, ' '); + format_address_paren(address + G_pseudo_offset, c); +} +#endif + +static void +dump_hexl_mode_trailer(size_t n_bytes, const char *block) +{ + fputs(" >", stdout); + while (n_bytes--) { + unsigned c = *(unsigned char *) block++; + c = (ISPRINT(c) ? c : '.'); + putchar(c); + } + putchar('<'); +} + +/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each + of the N_SPEC format specs. CURRENT_OFFSET is the byte address of + CURR_BLOCK in the concatenation of input files, and it is printed + (optionally) only before the output line associated with the first + format spec. When duplicate blocks are being abbreviated, the output + for a sequence of identical input blocks is the output for the first + block followed by an asterisk alone on a line. It is valid to compare + the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK. + That condition may be false only for the last input block -- and then + only when it has not been padded to length BYTES_PER_BLOCK. */ + +static void +write_block(off_t current_offset, size_t n_bytes, + const char *prev_block, const char *curr_block) +{ + unsigned i; + + if (!(option_mask32 & OPT_v) + && G.not_first + && n_bytes == G.bytes_per_block + && memcmp(prev_block, curr_block, G.bytes_per_block) == 0 + ) { + 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("*"); + G.prev_pair_equal = 1; + } + } else { + G.not_first = 1; + G.prev_pair_equal = 0; + for (i = 0; i < G.n_specs; i++) { + if (i == 0) + G.format_address(current_offset, '\0'); + else + printf("%*s", address_pad_len_char - '0', ""); + (*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[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); + } + putchar('\n'); + } + } +} + +static void +read_block(size_t n, char *block, size_t *n_bytes_in_buffer) +{ + assert(0 < n && n <= G.bytes_per_block); + + *n_bytes_in_buffer = 0; + + if (n == 0) + return; + + 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, G.in_stream); + *n_bytes_in_buffer += n_read; + if (n_read == n_needed) + break; + /* error check is done in check_and_close */ + check_and_close(); + open_next_file(); + } +} + +/* Return the least common multiple of the sizes associated + with the format specs. */ + +static int +get_lcm(void) +{ + size_t i; + int l_c_m = 1; + + 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; +} + +/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the + formatted block to standard output, and repeat until the specified + maximum number of bytes has been read or until all input has been + processed. If the last block read is smaller than BYTES_PER_BLOCK + and its size is not a multiple of the size associated with a format + spec, extend the input block with zero bytes until its length is a + multiple of all format spec sizes. Write the final block. Finally, + write on a line by itself the offset of the byte after the last byte + read. */ + +static void +dump(off_t current_offset, off_t end_offset) +{ + char *block[2]; + int idx; + size_t n_bytes_read; + + block[0] = xmalloc(2 * G.bytes_per_block); + block[1] = block[0] + G.bytes_per_block; + + idx = 0; + if (option_mask32 & OPT_N) { + while (1) { + size_t n_needed; + if (current_offset >= end_offset) { + n_bytes_read = 0; + break; + } + 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 < G.bytes_per_block) + break; + 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(G.bytes_per_block, block[idx], &n_bytes_read); + if (n_bytes_read < G.bytes_per_block) + break; + 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; + } + } + + if (n_bytes_read > 0) { + int l_c_m; + size_t bytes_to_write; + + l_c_m = get_lcm(); + + /* Make bytes_to_write the smallest multiple of l_c_m that + is at least as large as n_bytes_read. */ + bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m); + + memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); + write_block(current_offset, bytes_to_write, + block[idx ^ 1], block[idx]); + current_offset += n_bytes_read; + } + + G.format_address(current_offset, '\n'); + + if ((option_mask32 & OPT_N) && current_offset >= end_offset) + check_and_close(); + + free(block[0]); +} + +/* Read N bytes into BLOCK from the concatenation of the input files + named in the global array FILE_LIST. On the first call to this + function, the global variable IN_STREAM is expected to be an open + stream associated with the input file INPUT_FILENAME. If all N + bytes cannot be read from IN_STREAM, close IN_STREAM and update + the global variables IN_STREAM and INPUT_FILENAME. Then try to + read the remaining bytes from the newly opened file. Repeat if + necessary until EOF is reached for the last file in FILE_LIST. + On subsequent calls, don't modify BLOCK and return zero. Set + *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs, + it will be detected through ferror when the stream is about to be + closed. If there is an error, give a message but continue reading + as usual and return nonzero. Otherwise return zero. */ + +/* STRINGS mode. Find each "string constant" in the input. + A string constant is a run of at least 'string_min' ASCII + graphic (or formatting) characters terminated by a null. + Based on a function written by Richard Stallman for a + traditional version of od. */ + +static void +dump_strings(off_t address, off_t end_offset) +{ + unsigned bufsize = MAX(100, G.string_min); + unsigned char *buf = xmalloc(bufsize); + + while (1) { + size_t i; + int c; + + /* See if the next 'G.string_min' chars are all printing chars. */ + tryline: + if ((option_mask32 & OPT_N) && (end_offset - G.string_min <= address)) + break; + i = 0; + while (!(option_mask32 & OPT_N) || address < end_offset) { + if (i == bufsize) { + bufsize += bufsize/8; + buf = xrealloc(buf, bufsize); + } + + while (G.in_stream) { /* !EOF */ + c = fgetc(G.in_stream); + if (c != EOF) + goto got_char; + check_and_close(); + open_next_file(); + } + /* EOF */ + goto ret; + got_char: + address++; + if (!c) + break; + if (!ISPRINT(c)) + goto tryline; /* It isn't; give up on this string. */ + buf[i++] = c; /* String continues; store it all. */ + } + + if (i < G.string_min) /* Too short! */ + goto tryline; + + /* If we get here, the string is all printable and NUL-terminated */ + buf[i] = 0; + G.format_address(address - i - 1, ' '); + + for (i = 0; (c = buf[i]); i++) { + switch (c) { + case '\007': fputs("\\a", stdout); break; + case '\b': fputs("\\b", stdout); break; + case '\f': fputs("\\f", stdout); break; + case '\n': fputs("\\n", stdout); break; + case '\r': fputs("\\r", stdout); break; + case '\t': fputs("\\t", stdout); break; + case '\v': fputs("\\v", stdout); break; + default: putchar(c); + } + } + putchar('\n'); + } + + /* We reach this point only if we search through + (max_bytes_to_format - G.string_min) bytes before reaching EOF. */ + check_and_close(); + ret: + free(buf); +} + +#if ENABLE_LONG_OPTS +/* If S is a valid traditional offset specification with an optional + leading '+' return nonzero and set *OFFSET to the offset it denotes. */ + +static int +parse_old_offset(const char *s, off_t *offset) +{ + static const struct suffix_mult Bb[] = { + { "B", 1024 }, + { "b", 512 }, + { "", 0 } + }; + char *p; + int radix; + + /* Skip over any leading '+'. */ + if (s[0] == '+') ++s; + if (!isdigit(s[0])) return 0; /* not a number */ + + /* Determine the radix we'll use to interpret S. If there is a '.', + * it's decimal, otherwise, if the string begins with '0X'or '0x', + * it's hexadecimal, else octal. */ + p = strchr(s, '.'); + radix = 8; + if (p) { + p[0] = '\0'; /* cheating */ + radix = 10; + } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) + radix = 16; + + *offset = xstrtooff_sfx(s, radix, Bb); + if (p) p[0] = '.'; + + return (*offset >= 0); +} +#endif + +int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int od_main(int argc UNUSED_PARAM, char **argv) +{ +#if ENABLE_LONG_OPTS + static const char od_longopts[] ALIGN1 = + "skip-bytes\0" Required_argument "j" + "address-radix\0" Required_argument "A" + "read-bytes\0" Required_argument "N" + "format\0" Required_argument "t" + "output-duplicates\0" No_argument "v" + /* Yes, it's true: -S NUM, but --strings[=NUM]! + * that is, NUM is mandatory for -S but optional for --strings! + */ + "strings\0" Optional_argument "S" + "width\0" Optional_argument "w" + "traditional\0" No_argument "\xff" + ; +#endif + const char *str_A, *str_N, *str_j, *str_S = "3"; + llist_t *lst_t = NULL; + unsigned opt; + int l_c_m; + /* The number of input bytes to skip before formatting and writing. */ + off_t n_bytes_to_skip = 0; + /* The offset of the first byte after the last byte to be formatted. */ + off_t end_offset = 0; + /* The maximum number of bytes that will be formatted. */ + off_t max_bytes_to_format = 0; + + 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 = OD_GETOPT32(); + argv += optind; + if (opt & OPT_A) { + static const char doxn[] ALIGN1 = "doxn"; + static const char doxn_address_base_char[] ALIGN1 = { + 'u', 'o', 'x', /* '?' fourth one is not important */ + }; + static const uint8_t doxn_address_pad_len_char[] ALIGN1 = { + '7', '7', '6', /* '?' */ + }; + char *p; + int pos; + p = strchr(doxn, str_A[0]); + if (!p) + bb_error_msg_and_die("bad output address radix " + "'%c' (must be [doxn])", str_A[0]); + pos = p - doxn; + 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]; + } + if (opt & OPT_N) { + max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm_suffixes); + } + if (opt & OPT_a) decode_format_string("a"); + if (opt & OPT_b) decode_format_string("oC"); + if (opt & OPT_c) decode_format_string("c"); + if (opt & OPT_d) decode_format_string("u2"); + if (opt & OPT_f) decode_format_string("fF"); + if (opt & OPT_h) decode_format_string("x2"); + if (opt & OPT_i) decode_format_string("d2"); + if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm_suffixes); + if (opt & OPT_l) decode_format_string("d4"); + if (opt & OPT_o) decode_format_string("o2"); + while (lst_t) { + decode_format_string(llist_pop(&lst_t)); + } + if (opt & OPT_x) decode_format_string("x2"); + if (opt & OPT_s) decode_format_string("d2"); + if (opt & OPT_S) { + G.string_min = xstrtou_sfx(str_S, 0, bkm_suffixes); + } + + // Bloat: + //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 + * 0 to 3 remaining command line arguments; handle each case + * separately. + * od [FILE] [[+]OFFSET[.][b] [[+]LABEL[.][b]]] + * The offset and pseudo_start have the same syntax. + * + * FIXME: POSIX 1003.1-2001 with XSI requires support for the + * traditional syntax even if --traditional is not given. */ + +#if ENABLE_LONG_OPTS + if (opt & OPT_traditional) { + if (argv[0]) { + off_t pseudo_start = -1; + off_t o1, o2; + + if (!argv[1]) { /* one arg */ + if (parse_old_offset(argv[0], &o1)) { + /* od --traditional OFFSET */ + n_bytes_to_skip = o1; + argv++; + } + /* od --traditional FILE */ + } else if (!argv[2]) { /* two args */ + if (parse_old_offset(argv[0], &o1) + && parse_old_offset(argv[1], &o2) + ) { + /* od --traditional OFFSET LABEL */ + n_bytes_to_skip = o1; + pseudo_start = o2; + argv += 2; + } else if (parse_old_offset(argv[1], &o2)) { + /* od --traditional FILE OFFSET */ + n_bytes_to_skip = o2; + argv[1] = NULL; + } else { + bb_error_msg_and_die("invalid second argument '%s'", argv[1]); + } + } else if (!argv[3]) { /* three args */ + if (parse_old_offset(argv[1], &o1) + && parse_old_offset(argv[2], &o2) + ) { + /* od --traditional FILE OFFSET LABEL */ + n_bytes_to_skip = o1; + pseudo_start = o2; + argv[1] = NULL; + } else { + bb_error_msg_and_die("the last two arguments must be offsets"); + } + } else { /* >3 args */ + bb_error_msg_and_die("too many arguments"); + } + + if (pseudo_start >= 0) { + if (G.format_address == format_address_none) { + address_base_char = 'o'; + address_pad_len_char = '7'; + G.format_address = format_address_paren; + } else { + G.format_address = format_address_label; + } + G_pseudo_offset = pseudo_start - n_bytes_to_skip; + } + } + /* else: od --traditional (without args) */ + } +#endif + + if (option_mask32 & OPT_N) { + end_offset = n_bytes_to_skip + max_bytes_to_format; + if (end_offset < n_bytes_to_skip) + bb_error_msg_and_die("SKIP + SIZE is too large"); + } + + if (G.n_specs == 0) { + decode_format_string("o2"); + /*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: "-". */ + 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. */ + 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 (!G.in_stream) + return EXIT_FAILURE; + + /* Compute output block length */ + l_c_m = get_lcm(); + + if (opt & OPT_w) { /* -w: width */ + if (!G.bytes_per_block || G.bytes_per_block % l_c_m != 0) { + bb_error_msg("warning: invalid width %u; using %d instead", + (unsigned)G.bytes_per_block, l_c_m); + G.bytes_per_block = l_c_m; + } + } else { + G.bytes_per_block = l_c_m; + if (l_c_m < DEFAULT_BYTES_PER_BLOCK) + G.bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m; + } + +#ifdef DEBUG + { + int i; + for (i = 0; i < G.n_specs; i++) { + printf("%d: fmt='%s' width=%d\n", + i, G.spec[i].fmt_string, + width_bytes[G.spec[i].size]); + } + } +#endif + + if (option_mask32 & OPT_S) + dump_strings(n_bytes_to_skip, end_offset); + else + dump(n_bytes_to_skip, end_offset); + + if (fclose(stdin)) + bb_perror_msg_and_die(bb_msg_standard_input); + + return G.exit_code; +} diff --git a/busybox/coreutils/paste.c b/busybox/coreutils/paste.c new file mode 100644 index 00000000000000..52f67f9513d05e --- /dev/null +++ b/busybox/coreutils/paste.c @@ -0,0 +1,140 @@ +/* vi: set sw=4 ts=4: */ +/* + * paste.c - implementation of the posix paste command + * + * Written by Maxime Coste + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config PASTE +//config: bool "paste (4.5 kb)" +//config: default y +//config: help +//config: paste is used to paste lines of different files together +//config: and write the result to stdout + +//applet:IF_PASTE(APPLET_NOEXEC(paste, paste, BB_DIR_USR_BIN, BB_SUID_DROP, paste)) + +//kbuild:lib-$(CONFIG_PASTE) += paste.o + +//usage:#define paste_trivial_usage +//usage: "[OPTIONS] [FILE]..." +//usage:#define paste_full_usage "\n\n" +//usage: "Paste lines from each input file, separated with tab\n" +//usage: "\n -d LIST Use delimiters from LIST, not tab" +//usage: "\n -s Serial: one file at a time" +//usage: +//usage:#define paste_example_usage +//usage: "# write out directory in four columns\n" +//usage: "$ ls | paste - - - -\n" +//usage: "# combine pairs of lines from a file into single lines\n" +//usage: "$ paste -s -d '\\t\\n' file\n" + +#include "libbb.h" + +static void paste_files(FILE** files, int file_cnt, char* delims, int del_cnt) +{ + char *line; + char delim; + int active_files = file_cnt; + int i; + + while (active_files > 0) { + int del_idx = 0; + + for (i = 0; i < file_cnt; ++i) { + if (files[i] == NULL) + continue; + + line = xmalloc_fgetline(files[i]); + if (!line) { + fclose_if_not_stdin(files[i]); + files[i] = NULL; + --active_files; + continue; + } + fputs(line, stdout); + free(line); + delim = '\n'; + if (i != file_cnt - 1) { + delim = delims[del_idx++]; + if (del_idx == del_cnt) + del_idx = 0; + } + if (delim != '\0') + fputc(delim, stdout); + } + } +} + +static void paste_files_separate(FILE** files, char* delims, int del_cnt) +{ + char *line, *next_line; + char delim; + int i; + + for (i = 0; files[i]; ++i) { + int del_idx = 0; + + line = NULL; + while ((next_line = xmalloc_fgetline(files[i])) != NULL) { + if (line) { + fputs(line, stdout); + free(line); + delim = delims[del_idx++]; + if (del_idx == del_cnt) + del_idx = 0; + if (delim != '\0') + fputc(delim, stdout); + } + line = next_line; + } + if (line) { + /* coreutils adds \n even if this is a final line + * of the last file and it was not \n-terminated. + */ + printf("%s\n", line); + free(line); + } + fclose_if_not_stdin(files[i]); + } +} + +#define PASTE_OPT_DELIMITERS (1 << 0) +#define PASTE_OPT_SEPARATE (1 << 1) + +int paste_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int paste_main(int argc UNUSED_PARAM, char **argv) +{ + char *delims = (char*)"\t"; + int del_cnt = 1; + unsigned opt; + int i; + + opt = getopt32(argv, "d:s", &delims); + argv += optind; + + if (opt & PASTE_OPT_DELIMITERS) { + if (!delims[0]) + bb_error_msg_and_die("-d '' is not supported"); + /* unknown mappings are not changed: "\z" -> '\\' 'z' */ + /* trailing backslash, if any, is preserved */ + del_cnt = strcpy_and_process_escape_sequences(delims, delims) - delims; + /* note: handle NUL properly (do not stop at it!): try -d'\t\0\t' */ + } + + if (!argv[0]) + (--argv)[0] = (char*) "-"; + for (i = 0; argv[i]; ++i) { + argv[i] = (void*) fopen_or_warn_stdin(argv[i]); + if (!argv[i]) + xfunc_die(); + } + + if (opt & PASTE_OPT_SEPARATE) + paste_files_separate((FILE**)argv, delims, del_cnt); + else + paste_files((FILE**)argv, i, delims, del_cnt); + + fflush_stdout_and_exit(0); +} diff --git a/busybox/coreutils/printenv.c b/busybox/coreutils/printenv.c new file mode 100644 index 00000000000000..b0468b3f703e34 --- /dev/null +++ b/busybox/coreutils/printenv.c @@ -0,0 +1,59 @@ +/* vi: set sw=4 ts=4: */ +/* + * printenv implementation for busybox + * + * Copyright (C) 2005 by Erik Andersen + * Copyright (C) 2005 by Mike Frysinger + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config PRINTENV +//config: bool "printenv (1 kb)" +//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]..." +//usage:#define printenv_full_usage "\n\n" +//usage: "Print environment VARIABLEs.\n" +//usage: "If no VARIABLE specified, print all." + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int printenv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int printenv_main(int argc UNUSED_PARAM, char **argv) +{ + int exit_code = EXIT_SUCCESS; + + /* no variables specified, show whole env */ + if (!argv[1]) { + char **e = environ; + + /* environ can be NULL! (for example, after clearenv()) + * Check for that: + */ + if (e) + while (*e) + puts(*e++); + } else { + /* search for specified variables and print them out if found */ + char *arg, *env; + + while ((arg = *++argv) != NULL) { + env = getenv(arg); + if (env) + puts(env); + else + exit_code = EXIT_FAILURE; + } + } + + fflush_stdout_and_exit(exit_code); +} diff --git a/busybox/coreutils/printf.c b/busybox/coreutils/printf.c new file mode 100644 index 00000000000000..a666ff7acf43ef --- /dev/null +++ b/busybox/coreutils/printf.c @@ -0,0 +1,446 @@ +/* vi: set sw=4 ts=4: */ +/* + * printf - format and print data + * + * Copyright 1999 Dave Cinege + * Portions copyright (C) 1990-1996 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Usage: printf format [argument...] + * + * A front end to the printf function that lets it be used from the shell. + * + * Backslash escapes: + * + * \" = double quote + * \\ = backslash + * \a = alert (bell) + * \b = backspace + * \c = produce no further output + * \f = form feed + * \n = new line + * \r = carriage return + * \t = horizontal tab + * \v = vertical tab + * \0ooo = octal number (ooo is 0 to 3 digits) + * \xhhh = hexadecimal number (hhh is 1 to 3 digits) + * + * Additional directive: + * + * %b = print an argument string, interpreting backslash escapes + * + * The 'format' argument is re-used as many times as necessary + * to convert all of the given arguments. + * + * David MacKenzie + */ +/* 19990508 Busy Boxed! Dave Cinege */ + +//config:config PRINTF +//config: bool "printf (3.3 kb)" +//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 +//kbuild:lib-$(CONFIG_ASH_PRINTF) += printf.o +//kbuild:lib-$(CONFIG_HUSH_PRINTF) += printf.o + +//usage:#define printf_trivial_usage +//usage: "FORMAT [ARG]..." +//usage:#define printf_full_usage "\n\n" +//usage: "Format and print ARG(s) according to FORMAT (a-la C printf)" +//usage: +//usage:#define printf_example_usage +//usage: "$ printf \"Val=%d\\n\" 5\n" +//usage: "Val=5\n" + +#include "libbb.h" + +/* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it. + * They report it: + * bash: printf: XXX: invalid number + * printf: XXX: expected a numeric value + * bash: printf: 123XXX: invalid number + * printf: 123XXX: value not completely converted + * but then they use 0 (or partially converted numeric prefix) as a value + * and continue. They exit with 1 in this case. + * Both accept insane field width/precision (e.g. %9999999999.9999999999d). + * Both print error message and assume 0 if %*.*f width/precision is "bad" + * (but negative numbers are not "bad"). + * Both accept negative numbers for %u specifier. + * + * We try to be compatible. + */ + +typedef void FAST_FUNC (*converter)(const char *arg, void *result); + +static int multiconvert(const char *arg, void *result, converter convert) +{ + if (*arg == '"' || *arg == '\'') { + arg = utoa((unsigned char)arg[1]); + } + errno = 0; + convert(arg, result); + if (errno) { + bb_error_msg("invalid number '%s'", arg); + return 1; + } + return 0; +} + +static void FAST_FUNC conv_strtoull(const char *arg, void *result) +{ + *(unsigned long long*)result = bb_strtoull(arg, NULL, 0); + /* both coreutils 6.10 and bash 3.2: + * $ printf '%x\n' -2 + * fffffffffffffffe + * Mimic that: + */ + if (errno) { + *(unsigned long long*)result = bb_strtoll(arg, NULL, 0); + } +} +static void FAST_FUNC conv_strtoll(const char *arg, void *result) +{ + *(long long*)result = bb_strtoll(arg, NULL, 0); +} +static void FAST_FUNC conv_strtod(const char *arg, void *result) +{ + char *end; + /* Well, this one allows leading whitespace... so what? */ + /* What I like much less is that "-" accepted too! :( */ + *(double*)result = strtod(arg, &end); + if (end[0]) { + errno = ERANGE; + *(double*)result = 0; + } +} + +/* Callers should check errno to detect errors */ +static unsigned long long my_xstrtoull(const char *arg) +{ + unsigned long long result; + if (multiconvert(arg, &result, conv_strtoull)) + result = 0; + return result; +} +static long long my_xstrtoll(const char *arg) +{ + long long result; + if (multiconvert(arg, &result, conv_strtoll)) + result = 0; + return result; +} +static double my_xstrtod(const char *arg) +{ + double result; + multiconvert(arg, &result, conv_strtod); + return result; +} + +/* 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') { + str++; + if (c == '\\') { + /* %b also accepts 4-digit octals of the form \0### */ + if (*str == '0') { + if ((unsigned char)(str[1] - '0') < 8) { + /* 2nd char is 0..7: skip leading '0' */ + str++; + } + } + else if (*str == 'c') { + return 1; + } + { + /* optimization: don't force arg to be on-stack, + * use another variable for that. */ + const char *z = str; + c = bb_process_escape_sequence(&z); + str = z; + } + } + putchar(c); + } + + return 0; +} + +static void print_direc(char *format, unsigned fmt_length, + int field_width, int precision, + const char *argument) +{ + long long llv; + double dv; + char saved; + char *have_prec, *have_width; + + saved = format[fmt_length]; + format[fmt_length] = '\0'; + + have_prec = strstr(format, ".*"); + have_width = strchr(format, '*'); + if (have_width - 1 == have_prec) + have_width = NULL; + + errno = 0; + + switch (format[fmt_length - 1]) { + case 'c': + printf(format, *argument); + break; + case 'd': + case 'i': + llv = my_xstrtoll(argument); + print_long: + if (!have_width) { + if (!have_prec) + printf(format, llv); + else + printf(format, precision, llv); + } else { + if (!have_prec) + printf(format, field_width, llv); + else + printf(format, field_width, precision, llv); + } + break; + case 'o': + case 'u': + case 'x': + case 'X': + llv = my_xstrtoull(argument); + /* cheat: unsigned long and long have same width, so... */ + goto print_long; + case 's': + /* Are char* and long long the same? */ + if (sizeof(argument) == sizeof(llv)) { + llv = (long long)(ptrdiff_t)argument; + goto print_long; + } else { + /* Hope compiler will optimize it out by moving call + * instruction after the ifs... */ + if (!have_width) { + if (!have_prec) + printf(format, argument, /*unused:*/ argument, argument); + else + printf(format, precision, argument, /*unused:*/ argument); + } else { + if (!have_prec) + printf(format, field_width, argument, /*unused:*/ argument); + else + printf(format, field_width, precision, argument); + } + break; + } + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + dv = my_xstrtod(argument); + if (!have_width) { + if (!have_prec) + printf(format, dv); + else + printf(format, precision, dv); + } else { + if (!have_prec) + printf(format, field_width, dv); + else + printf(format, field_width, precision, dv); + } + break; + } /* switch */ + + format[fmt_length] = saved; +} + +/* Handle params for "%*.*f". Negative numbers are ok (compat). */ +static int get_width_prec(const char *str) +{ + int v = bb_strtoi(str, NULL, 10); + if (errno) { + bb_error_msg("invalid number '%s'", str); + v = 0; + } + return v; +} + +/* Print the text in FORMAT, using ARGV for arguments to any '%' directives. + Return advanced ARGV. */ +static char **print_formatted(char *f, char **argv, int *conv_err) +{ + char *direc_start; /* Start of % directive. */ + unsigned direc_length; /* Length of % directive. */ + int field_width; /* Arg to first '*' */ + int precision; /* Arg to second '*' */ + char **saved_argv = argv; + + for (; *f; ++f) { + switch (*f) { + case '%': + direc_start = f++; + direc_length = 1; + field_width = precision = 0; + if (*f == '%') { + bb_putchar('%'); + break; + } + if (*f == 'b') { + if (*argv) { + if (print_esc_string(*argv)) + return saved_argv; /* causes main() to exit */ + ++argv; + } + break; + } + if (*f && strchr("-+ #", *f)) { + ++f; + ++direc_length; + } + if (*f == '*') { + ++f; + ++direc_length; + if (*argv) + field_width = get_width_prec(*argv++); + } else { + while (isdigit(*f)) { + ++f; + ++direc_length; + } + } + if (*f == '.') { + ++f; + ++direc_length; + if (*f == '*') { + ++f; + ++direc_length; + if (*argv) + precision = get_width_prec(*argv++); + } else { + while (isdigit(*f)) { + ++f; + ++direc_length; + } + } + } + + /* Remove "lLhz" size modifiers, repeatedly. + * bash does not like "%lld", but coreutils + * happily takes even "%Llllhhzhhzd"! + * We are permissive like coreutils */ + while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { + overlapping_strcpy(f, f + 1); + } + /* Add "ll" if integer modifier, then print */ + { + static const char format_chars[] ALIGN1 = "diouxXfeEgGcs"; + char *p = strchr(format_chars, *f); + /* needed - try "printf %" without it */ + if (p == NULL || *f == '\0') { + bb_error_msg("%s: invalid format", direc_start); + /* causes main() to exit with error */ + return saved_argv - 1; + } + ++direc_length; + if (p - format_chars <= 5) { + /* it is one of "diouxX" */ + p = xmalloc(direc_length + 3); + memcpy(p, direc_start, direc_length); + p[direc_length + 1] = p[direc_length - 1]; + p[direc_length - 1] = 'l'; + p[direc_length] = 'l'; + //bb_error_msg("<%s>", p); + direc_length += 2; + direc_start = p; + } else { + p = NULL; + } + if (*argv) { + print_direc(direc_start, direc_length, field_width, + precision, *argv++); + } else { + print_direc(direc_start, direc_length, field_width, + precision, ""); + } + *conv_err |= errno; + free(p); + } + break; + case '\\': + if (*++f == 'c') { + return saved_argv; /* causes main() to exit */ + } + bb_putchar(bb_process_escape_sequence((const char **)&f)); + f--; + break; + default: + putchar(*f); + } + } + + return argv; +} + +int printf_main(int argc UNUSED_PARAM, char **argv) +{ + int conv_err; + char *format; + char **argv2; + + /* We must check that stdout is not closed. + * The reason for this is highly non-obvious. + * printf_main is used from shell. + * Shell must correctly handle 'printf "%s" foo' + * if stdout is closed. With stdio, output gets shoveled into + * stdout buffer, and even fflush cannot clear it out. It seems that + * even if libc receives EBADF on write attempts, it feels determined + * to output data no matter what. So it will try later, + * and possibly will clobber future output. Not good. */ +// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? + if (fcntl(1, F_GETFL) == -1) + return 1; /* match coreutils 6.10 (sans error msg to stderr) */ + //if (dup2(1, 1) != 1) - old way + // return 1; + + /* bash builtin errors out on "printf '-%s-\n' foo", + * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo". + * We will mimic coreutils. */ + if (argv[1] && argv[1][0] == '-' && argv[1][1] == '-' && !argv[1][2]) + argv++; + if (!argv[1]) { + if (ENABLE_ASH_PRINTF + && applet_name[0] != 'p' + ) { + bb_error_msg("usage: printf FORMAT [ARGUMENT...]"); + return 2; /* bash compat */ + } + bb_show_usage(); + } + + format = argv[1]; + argv2 = argv + 2; + + conv_err = 0; + do { + argv = argv2; + argv2 = print_formatted(format, argv, &conv_err); + } while (argv2 > argv && *argv2); + + /* coreutils compat (bash doesn't do this): + if (*argv) + fprintf(stderr, "excess args ignored"); + */ + + return (argv2 < argv) /* if true, print_formatted errored out */ + || conv_err; /* print_formatted saw invalid number */ +} diff --git a/busybox/coreutils/pwd.c b/busybox/coreutils/pwd.c new file mode 100644 index 00000000000000..345d8510045ba0 --- /dev/null +++ b/busybox/coreutils/pwd.c @@ -0,0 +1,93 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini pwd implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config PWD +//config: bool "pwd (3.4 kb)" +//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: "" +//usage:#define pwd_full_usage "\n\n" +//usage: "Print the full filename of the current working directory" +//usage: +//usage:#define pwd_example_usage +//usage: "$ pwd\n" +//usage: "/root\n" + +#include "libbb.h" + +static int logical_getcwd(void) +{ + struct stat st1; + struct stat st2; + char *wd; + char *p; + + wd = getenv("PWD"); + if (!wd || wd[0] != '/') + return 0; + + p = wd; + while (*p) { + /* doing strstr(p, "/.") by hand is smaller and faster... */ + if (*p++ != '/') + continue; + if (*p != '.') + continue; + /* we found "/.", skip to next char */ + p++; + if (*p == '.') + p++; /* we found "/.." */ + if (*p == '\0' || *p == '/') + return 0; /* "/./" or "/../" component: bad */ + } + + if (stat(wd, &st1) != 0) + return 0; + if (stat(".", &st2) != 0) + return 0; + if (st1.st_ino != st2.st_ino) + return 0; + if (st1.st_dev != st2.st_dev) + return 0; + + puts(wd); + return 1; +} + +int pwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int pwd_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + char *buf; + + if (ENABLE_DESKTOP) { + /* TODO: assume -L if $POSIXLY_CORRECT? (coreutils does that) + * Rationale: + * POSIX requires a default of -L, but most scripts expect -P + */ + unsigned opt = getopt32(argv, "LP"); + if ((opt & 1) && logical_getcwd()) + return fflush_all(); + } + + buf = xrealloc_getcwd_or_warn(NULL); + + if (buf) { + puts(buf); + free(buf); + return fflush_all(); + } + + return EXIT_FAILURE; +} diff --git a/busybox/coreutils/readlink.c b/busybox/coreutils/readlink.c new file mode 100644 index 00000000000000..49361cea03a899 --- /dev/null +++ b/busybox/coreutils/readlink.c @@ -0,0 +1,100 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini readlink implementation for busybox + * + * Copyright (C) 2000,2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config READLINK +//config: bool "readlink (3.6 kb)" +//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_NOFORK(readlink, readlink, BB_DIR_USR_BIN, BB_SUID_DROP, readlink)) + +//kbuild:lib-$(CONFIG_READLINK) += readlink.o + +//usage:#define readlink_trivial_usage +//usage: IF_FEATURE_READLINK_FOLLOW("[-fnv] ") "FILE" +//usage:#define readlink_full_usage "\n\n" +//usage: "Display the value of a symlink" +//usage: IF_FEATURE_READLINK_FOLLOW( "\n" +//usage: "\n -f Canonicalize by following all symlinks" +//usage: "\n -n Don't add newline" +//usage: "\n -v Verbose" +//usage: ) + +#include "libbb.h" + +/* + * # readlink --version + * readlink (GNU coreutils) 6.10 + * # readlink --help + * -f, --canonicalize + * canonicalize by following every symlink in + * every component of the given name recursively; + * all but the last component must exist + * -e, --canonicalize-existing + * canonicalize by following every symlink in + * every component of the given name recursively, + * all components must exist + * -m, --canonicalize-missing + * canonicalize by following every symlink in + * every component of the given name recursively, + * without requirements on components existence + * -n, --no-newline do not output the trailing newline + * -q, --quiet, -s, --silent suppress most error messages + * -v, --verbose report error messages + * + * bbox supports: -f (partially) -n -v (fully), -q -s (accepts but ignores) + * Note: we export the -f flag, but our -f behaves like coreutils' -e. + * Unfortunately, there isn't a C lib function we can leverage to get this + * behavior which means we'd have to implement the full stack ourselves :(. + */ + +int readlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int readlink_main(int argc UNUSED_PARAM, char **argv) +{ + char *buf; + char *fname; + + IF_FEATURE_READLINK_FOLLOW( + unsigned opt; + /* We need exactly one non-option argument. */ + opt = getopt32(argv, "^" "fnvsq" "\0" "=1"); + fname = argv[optind]; + ) + IF_NOT_FEATURE_READLINK_FOLLOW( + const unsigned opt = 0; + if (argc != 2) bb_show_usage(); + fname = argv[1]; + ) + + /* compat: coreutils readlink reports errors silently via exit code */ + if (!(opt & 4)) /* not -v */ + logmode = LOGMODE_NONE; + + /* NOFORK: only one alloc is allowed; must free */ + if (opt & 1) { /* -f */ + buf = xmalloc_realpath_coreutils(fname); + } else { + buf = xmalloc_readlink_or_warn(fname); + } + + if (!buf) + return EXIT_FAILURE; + printf((opt & 2) ? "%s" : "%s\n", buf); + free(buf); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/realpath.c b/busybox/coreutils/realpath.c new file mode 100644 index 00000000000000..43923681c43415 --- /dev/null +++ b/busybox/coreutils/realpath.c @@ -0,0 +1,52 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Now does proper error checking on output and returns a failure exit code + * if one or more paths cannot be resolved. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config REALPATH +//config: bool "realpath (1.1 kb)" +//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_NOFORK(realpath, realpath, BB_DIR_USR_BIN, BB_SUID_DROP, realpath)) + +//kbuild:lib-$(CONFIG_REALPATH) += realpath.o + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */ + +//usage:#define realpath_trivial_usage +//usage: "FILE..." +//usage:#define realpath_full_usage "\n\n" +//usage: "Return the absolute pathnames of given FILE" + +#include "libbb.h" + +int realpath_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int realpath_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + + if (!*++argv) { + bb_show_usage(); + } + + do { + /* NOFORK: only one alloc is allowed; must free */ + char *resolved_path = xmalloc_realpath_coreutils(*argv); + if (resolved_path != NULL) { + puts(resolved_path); + free(resolved_path); + } else { + retval = EXIT_FAILURE; + bb_simple_perror_msg(*argv); + } + } while (*++argv); + + fflush_stdout_and_exit(retval); +} diff --git a/busybox/coreutils/rm.c b/busybox/coreutils/rm.c new file mode 100644 index 00000000000000..b68a82dc42d95e --- /dev/null +++ b/busybox/coreutils/rm.c @@ -0,0 +1,76 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rm implementation for busybox + * + * Copyright (C) 2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. + */ +//config:config RM +//config: bool "rm (4.9 kb)" +//config: default y +//config: help +//config: rm is used to remove files or directories. + +//applet:IF_RM(APPLET_NOEXEC(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm)) +/* was NOFORK, but then "rm -i FILE" can't be ^C'ed if run by hush */ + +//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..." +//usage:#define rm_full_usage "\n\n" +//usage: "Remove (unlink) FILEs\n" +//usage: "\n -i Always prompt before removing" +//usage: "\n -f Never prompt" +//usage: "\n -R,-r Recurse" +//usage: +//usage:#define rm_example_usage +//usage: "$ rm -rf /tmp/foo\n" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +int rm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int rm_main(int argc UNUSED_PARAM, char **argv) +{ + int status = 0; + int flags = 0; + unsigned opt; + + opt = getopt32(argv, "^" "fiRrv" "\0" "f-i:i-f"); + argv += optind; + if (opt & 1) + flags |= FILEUTILS_FORCE; + if (opt & 2) + flags |= FILEUTILS_INTERACTIVE; + if (opt & (8|4)) + flags |= FILEUTILS_RECUR; + if ((opt & 16) && FILEUTILS_VERBOSE) + flags |= FILEUTILS_VERBOSE; + + if (*argv != NULL) { + do { + const char *base = bb_get_last_path_component_strip(*argv); + + if (DOT_OR_DOTDOT(base)) { + bb_error_msg("can't remove '.' or '..'"); + } else if (remove_file(*argv, flags) >= 0) { + continue; + } + status = 1; + } while (*++argv); + } else if (!(flags & FILEUTILS_FORCE)) { + bb_show_usage(); + } + + return status; +} diff --git a/busybox/coreutils/rmdir.c b/busybox/coreutils/rmdir.c new file mode 100644 index 00000000000000..95574049495566 --- /dev/null +++ b/busybox/coreutils/rmdir.c @@ -0,0 +1,95 @@ +/* vi: set sw=4 ts=4: */ +/* + * rmdir implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config RMDIR +//config: bool "rmdir (3.4 kb)" +//config: default y +//config: help +//config: rmdir is used to remove empty directories. + +//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 */ + +//usage:#define rmdir_trivial_usage +//usage: "[OPTIONS] DIRECTORY..." +//usage:#define rmdir_full_usage "\n\n" +//usage: "Remove DIRECTORY if it is empty\n" +//usage: "\n -p Include parents" +//usage: IF_LONG_OPTS( +//usage: "\n --ignore-fail-on-non-empty" +//usage: ) +//usage: +//usage:#define rmdir_example_usage +//usage: "# rmdir /tmp/foo\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + + +#define PARENTS (1 << 0) +#define VERBOSE ((1 << 1) * ENABLE_FEATURE_VERBOSE) +#define IGNORE_NON_EMPTY ((1 << 2) * ENABLE_LONG_OPTS) + +int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int rmdir_main(int argc UNUSED_PARAM, char **argv) +{ + int status = EXIT_SUCCESS; + int flags; + char *path; + + flags = getopt32long(argv, "pv", + "parents\0" No_argument "p" + /* 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" + ) + ); + argv += optind; + + if (!*argv) { + bb_show_usage(); + } + + do { + path = *argv; + + while (1) { + if (flags & VERBOSE) { + printf("rmdir: removing directory, '%s'\n", path); + } + + if (rmdir(path) < 0) { +#if ENABLE_LONG_OPTS + if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY) + break; +#endif + bb_perror_msg("'%s'", path); /* Match gnu rmdir msg. */ + status = EXIT_FAILURE; + } else if (flags & PARENTS) { + /* Note: path was not "" since rmdir succeeded. */ + path = dirname(path); + /* Path is now just the parent component. Dirname + * returns "." if there are no parents. + */ + if (NOT_LONE_CHAR(path, '.')) { + continue; + } + } + break; + } + } while (*++argv); + + return status; +} diff --git a/busybox/coreutils/seq.c b/busybox/coreutils/seq.c new file mode 100644 index 00000000000000..c26ff06b9a575f --- /dev/null +++ b/busybox/coreutils/seq.c @@ -0,0 +1,119 @@ +/* vi: set sw=4 ts=4: */ +/* + * seq implementation for busybox + * + * Copyright (C) 2004, Glenn McGrath + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config SEQ +//config: bool "seq (3.6 kb)" +//config: default y +//config: help +//config: print a sequence of numbers + +//applet:IF_SEQ(APPLET_NOEXEC(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq)) +/* was NOFORK, but then "seq 1 999999999" can't be ^C'ed if run by hush */ + +//kbuild:lib-$(CONFIG_SEQ) += seq.o + +//usage:#define seq_trivial_usage +//usage: "[-w] [-s SEP] [FIRST [INC]] LAST" +//usage:#define seq_full_usage "\n\n" +//usage: "Print numbers from FIRST to LAST, in steps of INC.\n" +//usage: "FIRST, INC default to 1.\n" +//usage: "\n -w Pad to last with leading zeros" +//usage: "\n -s SEP String separator" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +int seq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int seq_main(int argc, char **argv) +{ + enum { + OPT_w = (1 << 0), + OPT_s = (1 << 1), + }; + double first, last, increment, v; + unsigned n; + unsigned width; + unsigned frac_part; + const char *sep, *opt_s = "\n"; + unsigned opt; + +#if ENABLE_LOCALE_SUPPORT + /* Undo busybox.c: on input, we want to use dot + * as fractional separator, regardless of current locale */ + setlocale(LC_NUMERIC, "C"); +#endif + + opt = getopt32(argv, "+ws:", &opt_s); + argc -= optind; + argv += optind; + first = increment = 1; + errno = 0; + switch (argc) { + char *pp; + case 3: + increment = strtod(argv[1], &pp); + errno |= *pp; + case 2: + first = strtod(argv[0], &pp); + errno |= *pp; + case 1: + last = strtod(argv[argc-1], &pp); + if (!errno && *pp == '\0') + break; + default: + bb_show_usage(); + } + +#if ENABLE_LOCALE_SUPPORT + setlocale(LC_NUMERIC, ""); +#endif + + /* Last checked to be compatible with: coreutils-6.10 */ + width = 0; + frac_part = 0; + while (1) { + char *dot = strchrnul(*argv, '.'); + int w = (dot - *argv); + int f = strlen(dot); + if (width < w) + width = w; + argv++; + if (!*argv) + break; + /* Why do the above _before_ frac check below? + * Try "seq 1 2.0" and "seq 1.0 2.0": + * coreutils never pay attention to the number + * of fractional digits in last arg. */ + if (frac_part < f) + frac_part = f; + } + if (frac_part) { + frac_part--; + if (frac_part) + width += frac_part + 1; + } + if (!(opt & OPT_w)) + width = 0; + + sep = ""; + v = first; + n = 0; + while (increment >= 0 ? v <= last : v >= last) { + if (printf("%s%0*.*f", sep, width, frac_part, v) < 0) + break; /* I/O error, bail out (yes, this really happens) */ + sep = opt_s; + /* v += increment; - would accumulate floating point errors */ + n++; + v = first + n * increment; + } + if (n) /* if while loop executed at least once */ + bb_putchar('\n'); + + return fflush_all(); +} diff --git a/busybox/coreutils/shred.c b/busybox/coreutils/shred.c new file mode 100644 index 00000000000000..3ceb58d195628a --- /dev/null +++ b/busybox/coreutils/shred.c @@ -0,0 +1,106 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2017 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config SHRED +//config: bool "shred (5 kb)" +//config: default y +//config: help +//config: Overwrite a file to hide its contents, and optionally delete it + +//applet:IF_SHRED(APPLET(shred, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SHRED) += shred.o + +//usage:#define shred_trivial_usage +//usage: "FILE..." +//usage:#define shred_full_usage "\n\n" +//usage: "Overwrite/delete FILEs\n" +//usage: "\n -f Chmod to ensure writability" +//usage: "\n -n N Overwrite N times (default 3)" +//usage: "\n -z Final overwrite with zeros" +//usage: "\n -u Remove file" +//-x and -v are accepted but have no effect + +/* shred (GNU coreutils) 8.25: +-f, --force change permissions to allow writing if necessary +-u truncate and remove file after overwriting +-z, --zero add a final overwrite with zeros to hide shredding +-n, --iterations=N overwrite N times instead of the default (3) +-v, --verbose show progress +-x, --exact do not round file sizes up to the next full block; this is the default for non-regular files +--random-source=FILE get random bytes from FILE +-s, --size=N shred this many bytes (suffixes like K, M, G accepted) +--remove[=HOW] like -u but give control on HOW to delete; See below +*/ + +#include "libbb.h" + +int shred_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int shred_main(int argc UNUSED_PARAM, char **argv) +{ + int rand_fd = rand_fd; /* for compiler */ + int zero_fd; + unsigned num_iter = 3; + unsigned opt; + enum { + OPT_f = (1 << 0), + OPT_u = (1 << 1), + OPT_z = (1 << 2), + OPT_n = (1 << 3), + OPT_v = (1 << 4), + OPT_x = (1 << 5), + }; + + opt = getopt32(argv, "fuzn:+vx", &num_iter); + argv += optind; + + zero_fd = xopen("/dev/zero", O_RDONLY); + if (num_iter != 0) + rand_fd = xopen("/dev/urandom", O_RDONLY); + + if (!*argv) + bb_show_usage(); + + for (;;) { + struct stat sb; + const char *fname; + unsigned i; + int fd; + + fname = *argv++; + if (!fname) + break; + fd = -1; + if (opt & OPT_f) { + fd = open(fname, O_WRONLY); + if (fd < 0) + chmod(fname, 0666); + } + if (fd < 0) + fd = xopen(fname, O_WRONLY); + + if (fstat(fd, &sb) == 0 && sb.st_size > 0) { + off_t size = sb.st_size; + + for (i = 0; i < num_iter; i++) { + bb_copyfd_size(rand_fd, fd, size); + fdatasync(fd); + xlseek(fd, 0, SEEK_SET); + } + if (opt & OPT_z) { + bb_copyfd_size(zero_fd, fd, size); + fdatasync(fd); + } + if (opt & OPT_u) { + ftruncate(fd, 0); + xunlink(fname); + } + xclose(fd); + } + } + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/shuf.c b/busybox/coreutils/shuf.c new file mode 100644 index 00000000000000..fdbd3e9b2ebca4 --- /dev/null +++ b/busybox/coreutils/shuf.c @@ -0,0 +1,156 @@ +/* vi: set sw=4 ts=4: */ +/* + * shuf: Write a random permutation of the input lines to standard output. + * + * Copyright (C) 2014 by Bartosz Golaszewski + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config SHUF +//config: bool "shuf (5.4 kb)" +//config: default y +//config: help +//config: Generate random permutations + +//applet:IF_SHUF(APPLET_NOEXEC(shuf, shuf, BB_DIR_USR_BIN, BB_SUID_DROP, shuf)) + +//kbuild:lib-$(CONFIG_SHUF) += shuf.o + +//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 + 1; + 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; + + opts = getopt32(argv, "^" + OPT_STR + "\0" "e--i:i--e"/* mutually exclusive */, + &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/busybox/coreutils/sleep.c b/busybox/coreutils/sleep.c new file mode 100644 index 00000000000000..9b9581ca9a68b3 --- /dev/null +++ b/busybox/coreutils/sleep.c @@ -0,0 +1,154 @@ +/* vi: set sw=4 ts=4: */ +/* + * sleep implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* 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 (1.7 kb)" +//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("]...") +//usage:#define sleep_full_usage "\n\n" +//usage: IF_NOT_FEATURE_FANCY_SLEEP("Pause for N seconds") +//usage: IF_FEATURE_FANCY_SLEEP( +//usage: "Pause for a time equal to the total of the args given, where each arg can\n" +//usage: "have an optional suffix of (s)econds, (m)inutes, (h)ours, or (d)ays") +//usage: +//usage:#define sleep_example_usage +//usage: "$ sleep 2\n" +//usage: "[2 second delay results]\n" +//usage: IF_FEATURE_FANCY_SLEEP( +//usage: "$ sleep 1d 3h 22m 8s\n" +//usage: "[98528 second delay results]\n") + +#include "libbb.h" + +#if ENABLE_FEATURE_FANCY_SLEEP || ENABLE_FEATURE_FLOAT_SLEEP +static const struct suffix_mult sfx[] = { + { "s", 1 }, + { "m", 60 }, + { "h", 60*60 }, + { "d", 24*60*60 }, + { "", 0 } +}; +#endif + +int sleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sleep_main(int argc UNUSED_PARAM, char **argv) +{ +#if ENABLE_FEATURE_FLOAT_SLEEP + double duration; + struct timespec ts; +#else + unsigned duration; +#endif + + ++argv; + if (!*argv) + bb_show_usage(); + +#if ENABLE_FEATURE_FLOAT_SLEEP + +# if ENABLE_LOCALE_SUPPORT + /* undo busybox.c setlocale */ + setlocale(LC_NUMERIC, "C"); +# endif + duration = 0; + do { + char *arg = *argv; + if (strchr(arg, '.')) { + double d; + char *pp; + int len = strspn(arg, "0123456789."); + char sv = arg[len]; + arg[len] = '\0'; + errno = 0; + d = strtod(arg, &pp); + if (errno || *pp) + bb_show_usage(); + arg += len; + *arg-- = sv; + sv = *arg; + *arg = '1'; + duration += d * xatoul_sfx(arg, sfx); + *arg = sv; + } else { + duration += xatoul_sfx(arg, sfx); + } + } while (*++argv); + + ts.tv_sec = MAXINT(typeof(ts.tv_sec)); + ts.tv_nsec = 0; + if (duration >= 0 && duration < ts.tv_sec) { + ts.tv_sec = duration; + ts.tv_nsec = (duration - ts.tv_sec) * 1000000000; + } + do { + errno = 0; + nanosleep(&ts, &ts); + } while (errno == EINTR); + +#elif ENABLE_FEATURE_FANCY_SLEEP + + duration = 0; + do { + duration += xatou_range_sfx(*argv, 0, UINT_MAX - duration, sfx); + } while (*++argv); + sleep(duration); + +#else /* simple */ + + duration = xatou(*argv); + sleep(duration); + // Off. If it's really needed, provide example why + //if (sleep(duration)) { + // bb_perror_nomsg_and_die(); + //} + +#endif + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/sort.c b/busybox/coreutils/sort.c new file mode 100644 index 00000000000000..05e5c9071e859d --- /dev/null +++ b/busybox/coreutils/sort.c @@ -0,0 +1,647 @@ +/* vi: set sw=4 ts=4: */ +/* + * SuS3 compliant sort implementation for busybox + * + * Copyright (C) 2004 by Rob Landley + * + * MAINTAINER: Rob Landley + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * See SuS3 sort standard at: + * http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html + */ +//config:config SORT +//config: bool "sort (7.4 kb)" +//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 -ktcbdfiogM)" +//config: default y +//config: depends on SORT +//config: help +//config: Without this, sort only supports -rusz, 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 +//config: +//config:config FEATURE_SORT_OPTIMIZE_MEMORY +//config: bool "Use less memory (but might be slower)" +//config: default n # defaults to N since we are size-paranoid tribe +//config: depends on SORT +//config: help +//config: Attempt to use less memory (by storing only one copy +//config: of duplicated lines, and such). Useful if you work on huge files. + +//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("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 -o FILE Output to FILE" +//usage: "\n -c Check whether input is sorted" +//usage: "\n -b Ignore leading blanks" +//usage: "\n -f Ignore case" +//usage: "\n -i Ignore unprintable characters" +//usage: "\n -d Dictionary order (blank or alphanumeric only)" +//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 -g General numerical sort" +//usage: "\n -M Sort month" +//usage: "\n -V Sort version" +//usage: "\n -t CHAR Field separator" +//usage: "\n -k N[,M] Sort by Nth field" +//usage: ) +//usage: "\n -r Reverse sort order" +//usage: "\n -s Stable (don't sort ties alphabetically)" +//usage: "\n -u Suppress duplicate lines" +//usage: "\n -z Lines are terminated by NUL, not newline" +///////: "\n -m Ignored for GNU compatibility" +///////: "\n -S BUFSZ Ignored for GNU compatibility" +///////: "\n -T TMPDIR Ignored for GNU compatibility" +//usage: +//usage:#define sort_example_usage +//usage: "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n" +//usage: "a\n" +//usage: "b\n" +//usage: "c\n" +//usage: "d\n" +//usage: "e\n" +//usage: "f\n" +//usage: IF_FEATURE_SORT_BIG( +//usage: "$ echo -e \"c 3\\nb 2\\nd 2\" | $SORT -k 2,2n -k 1,1r\n" +//usage: "d 2\n" +//usage: "b 2\n" +//usage: "c 3\n" +//usage: ) +//usage: "" + +#include "libbb.h" + +/* These are sort types */ +enum { + FLAG_n = 1 << 0, /* Numeric sort */ + FLAG_g = 1 << 1, /* Sort using strtod() */ + FLAG_M = 1 << 2, /* Sort date */ + FLAG_V = 1 << 3, /* Sort version */ +/* ucsz apply to root level only, not keys. b at root level implies bb */ + FLAG_u = 1 << 4, /* Unique */ + FLAG_c = 1 << 5, /* Check: no output, exit(!ordered) */ + FLAG_s = 1 << 6, /* Stable sort, no ascii fallback at end */ + FLAG_z = 1 << 7, /* Input and output is NUL terminated, not \n */ +/* These can be applied to search keys, the previous four can't */ + FLAG_b = 1 << 8, /* Ignore leading blanks */ + FLAG_r = 1 << 9, /* Reverse */ + FLAG_d = 1 << 10, /* Ignore !(isalnum()|isspace()) */ + FLAG_f = 1 << 11, /* Force uppercase */ + FLAG_i = 1 << 12, /* Ignore !isprint() */ + FLAG_m = 1 << 13, /* ignored: merge already sorted files; do not sort */ + FLAG_S = 1 << 14, /* ignored: -S, --buffer-size=SIZE */ + FLAG_T = 1 << 15, /* ignored: -T, --temporary-directory=DIR */ + FLAG_o = 1 << 16, + FLAG_k = 1 << 17, + FLAG_t = 1 << 18, + FLAG_bb = 0x80000000, /* Ignore trailing blanks */ + FLAG_no_tie_break = 0x40000000, +}; + +static const char sort_opt_str[] ALIGN1 = "^" + "ngMVucszbrdfimS:T:o:k:*t:" + "\0" "o--o:t--t"/*-t, -o: at most one of each*/; +/* + * OPT_STR must not be string literal, needs to have stable address: + * code uses "strchr(OPT_STR,c) - OPT_STR" idiom. + */ +#define OPT_STR (sort_opt_str + 1) + +#if ENABLE_FEATURE_SORT_BIG +static char key_separator; + +static struct sort_key { + struct sort_key *next_key; /* linked list */ + unsigned range[4]; /* start word, start char, end word, end char */ + unsigned flags; +} *key_list; + + +/* This is a NOEXEC applet. Be very careful! */ + + +static char *get_key(char *str, struct sort_key *key, int flags) +{ + 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 */ + if (key->range[0] == 1 && !key->range[1] && !key->range[2] && !key->range[3] + && !(flags & (FLAG_b | FLAG_d | FLAG_f | FLAG_i | FLAG_bb)) + ) { + return str; + } + + /* Find start of key on first pass, end on second pass */ + len = strlen(str); + for (j = 0; j < 2; j++) { + if (!key->range[2*j]) + 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 ((ch = str[end]) != '\0') { + end++; + if (ch == key_separator) + break; + } + } else { + /* Skip leading blanks */ + while (isspace(str[end])) + end++; + /* Skip body of key */ + 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 */ + 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--; + /* -kSTART,N.ENDCHAR: honor ENDCHAR (1-based) */ + if (key->range[3]) { + 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; + str = xstrndup(str+start, end-start); + /* Handle -d */ + if (flags & FLAG_d) { + for (start = end = 0; str[end]; end++) + if (isspace(str[end]) || isalnum(str[end])) + str[start++] = str[end]; + str[start] = '\0'; + } + /* Handle -i */ + if (flags & FLAG_i) { + for (start = end = 0; str[end]; end++) + if (isprint_asciionly(str[end])) + str[start++] = str[end]; + str[start] = '\0'; + } + /* Handle -f */ + if (flags & FLAG_f) + for (i = 0; str[i]; i++) + str[i] = toupper(str[i]); + + return str; +} + +static struct sort_key *add_key(void) +{ + struct sort_key **pkey = &key_list; + while (*pkey) + pkey = &((*pkey)->next_key); + return *pkey = xzalloc(sizeof(struct sort_key)); +} + +#define GET_LINE(fp) \ + ((option_mask32 & FLAG_z) \ + ? bb_get_chunk_from_file(fp, NULL) \ + : xmalloc_fgetline(fp)) +#else +#define GET_LINE(fp) xmalloc_fgetline(fp) +#endif + +/* Iterate through keys list and perform comparisons */ +static int compare_keys(const void *xarg, const void *yarg) +{ + int flags = option_mask32, retval = 0; + char *x, *y; + +#if ENABLE_FEATURE_SORT_BIG + struct sort_key *key; + + for (key = key_list; !retval && key; key = key->next_key) { + flags = key->flags ? key->flags : option_mask32; + /* Chop out and modify key chunks, handling -dfib */ + x = get_key(*(char **)xarg, key, flags); + y = get_key(*(char **)yarg, key, flags); +#else + /* This curly bracket serves no purpose but to match the nesting + * level of the for () loop we're not using */ + { + x = *(char **)xarg; + y = *(char **)yarg; +#endif + /* Perform actual comparison */ + switch (flags & (FLAG_n | FLAG_g | FLAG_M | FLAG_V)) { + default: + bb_error_msg_and_die("unknown sort type"); + break; +#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1 + case FLAG_V: + retval = strverscmp(x, y); + break; +#endif + /* Ascii sort */ + case 0: +#if ENABLE_LOCALE_SUPPORT + retval = strcoll(x, y); +#else + retval = strcmp(x, y); +#endif + break; +#if ENABLE_FEATURE_SORT_BIG + case FLAG_g: { + char *xx, *yy; + double dx = strtod(x, &xx); + double dy = strtod(y, &yy); + /* not numbers < NaN < -infinity < numbers < +infinity) */ + if (x == xx) + retval = (y == yy ? 0 : -1); + else if (y == yy) + retval = 1; + /* Check for isnan */ + else if (dx != dx) + retval = (dy != dy) ? 0 : -1; + else if (dy != dy) + retval = 1; + /* Check for infinity. Could underflow, but it avoids libm. */ + else if (1.0 / dx == 0.0) { + if (dx < 0) + retval = (1.0 / dy == 0.0 && dy < 0) ? 0 : -1; + else + retval = (1.0 / dy == 0.0 && dy > 0) ? 0 : 1; + } else if (1.0 / dy == 0.0) + retval = (dy < 0) ? 1 : -1; + else + retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); + break; + } + case FLAG_M: { + struct tm thyme; + int dx; + char *xx, *yy; + + xx = strptime(x, "%b", &thyme); + dx = thyme.tm_mon; + yy = strptime(y, "%b", &thyme); + if (!xx) + retval = (!yy) ? 0 : -1; + else if (!yy) + retval = 1; + else + retval = dx - thyme.tm_mon; + break; + } + /* Full floating point version of -n */ + case FLAG_n: { + double dx = atof(x); + double dy = atof(y); + retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); + break; + } + } /* switch */ + /* Free key copies. */ + if (x != *(char **)xarg) free(x); + if (y != *(char **)yarg) free(y); + /* if (retval) break; - done by for () anyway */ +#else + /* Integer version of -n for tiny systems */ + case FLAG_n: + retval = atoi(x) - atoi(y); + break; + } /* switch */ +#endif + } /* for */ + + if (retval == 0) { + /* So far lines are "the same" */ + + if (option_mask32 & FLAG_s) { + /* "Stable sort": later line is "greater than", + * IOW: do not allow qsort() to swap equal lines. + */ + uint32_t *p32; + uint32_t x32, y32; + char *line; + unsigned len; + + line = *(char**)xarg; + len = (strlen(line) + 4) & (~3u); + p32 = (void*)(line + len); + x32 = *p32; + line = *(char**)yarg; + len = (strlen(line) + 4) & (~3u); + p32 = (void*)(line + len); + y32 = *p32; + + /* If x > y, 1, else -1 */ + retval = (x32 > y32) * 2 - 1; + } else + if (!(option_mask32 & FLAG_no_tie_break)) { + /* fallback sort */ + flags = option_mask32; + retval = strcmp(*(char **)xarg, *(char **)yarg); + } + } + + if (flags & FLAG_r) + return -retval; + + return retval; +} + +#if ENABLE_FEATURE_SORT_BIG +static unsigned str2u(char **str) +{ + unsigned long lu; + if (!isdigit((*str)[0])) + bb_error_msg_and_die("bad field specification"); + lu = strtoul(*str, str, 10); + if ((sizeof(long) > sizeof(int) && lu > INT_MAX) || !lu) + bb_error_msg_and_die("bad field specification"); + return lu; +} +#endif + +int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sort_main(int argc UNUSED_PARAM, char **argv) +{ + char **lines; + char *str_ignored, *str_o, *str_t; + llist_t *lst_k = NULL; + int i; + int linecount; + unsigned opts; +#if ENABLE_FEATURE_SORT_OPTIMIZE_MEMORY + bool can_drop_dups; + size_t prev_len = 0; + char *prev_line = (char*) ""; + /* Postpone optimizing if the input is small, < 16k lines: + * even just free()ing duplicate lines takes time. + */ + size_t count_to_optimize_dups = 0x3fff; +#endif + + xfunc_error_retval = 2; + + /* Parse command line options */ + opts = getopt32(argv, + sort_opt_str, + &str_ignored, &str_ignored, &str_o, &lst_k, &str_t + ); +#if ENABLE_FEATURE_SORT_OPTIMIZE_MEMORY + /* Can drop dups only if -u but no "complicating" options, + * IOW: if we do a full line compares. Safe options: + * -o FILE Output to FILE + * -z Lines are terminated by NUL, not newline + * -r Reverse sort order + * -s Stable (don't sort ties alphabetically) + * Not sure about these: + * -b Ignore leading blanks + * -f Ignore case + * -i Ignore unprintable characters + * -d Dictionary order (blank or alphanumeric only) + * -n Sort numbers + * -g General numerical sort + * -M Sort month + */ + can_drop_dups = ((opts & ~(FLAG_o|FLAG_z|FLAG_r|FLAG_s)) == FLAG_u); + /* Stable sort needs every line to be uniquely allocated, + * disable optimization to reuse strings: + */ + if (opts & FLAG_s) + count_to_optimize_dups = (size_t)-1L; +#endif + /* global b strips leading and trailing spaces */ + if (opts & FLAG_b) + option_mask32 |= FLAG_bb; +#if ENABLE_FEATURE_SORT_BIG + if (opts & FLAG_t) { + if (!str_t[0] || str_t[1]) + bb_error_msg_and_die("bad -t parameter"); + key_separator = str_t[0]; + } + /* note: below this point we use option_mask32, not opts, + * since that reduces register pressure and makes code smaller */ + + /* Parse sort key */ + while (lst_k) { + enum { + FLAG_allowed_for_k = + FLAG_n | /* Numeric sort */ + FLAG_g | /* Sort using strtod() */ + FLAG_M | /* Sort date */ + FLAG_b | /* Ignore leading blanks */ + FLAG_r | /* Reverse */ + FLAG_d | /* Ignore !(isalnum()|isspace()) */ + FLAG_f | /* Force uppercase */ + FLAG_i | /* Ignore !isprint() */ + 0 + }; + struct sort_key *key = add_key(); + char *str_k = llist_pop(&lst_k); + + i = 0; /* i==0 before comma, 1 after (-k3,6) */ + while (*str_k) { + /* Start of range */ + /* Cannot use bb_strtou - suffix can be a letter */ + key->range[2*i] = str2u(&str_k); + if (*str_k == '.') { + str_k++; + key->range[2*i+1] = str2u(&str_k); + } + while (*str_k) { + 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 */ + idx = strchr(OPT_STR, *str_k); + if (!idx) + bb_error_msg_and_die("unknown key option"); + 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 */ + if (i && flag == FLAG_b) + flag = FLAG_bb; + key->flags |= flag; + str_k++; + } + } + } +#endif + + /* Open input files and read data */ + argv += optind; + if (!*argv) + *--argv = (char*)"-"; + linecount = 0; + lines = NULL; + do { + /* coreutils 6.9 compat: abort on first open error, + * do not continue to next file: */ + FILE *fp = xfopen_stdin(*argv); + for (;;) { + char *line = GET_LINE(fp); + if (!line) + break; + +#if ENABLE_FEATURE_SORT_OPTIMIZE_MEMORY + if (count_to_optimize_dups != 0) + count_to_optimize_dups--; + if (count_to_optimize_dups == 0) { + size_t len; + char *new_line; + + /* On kernel/linux/arch/ *.[ch] files, + * this reduces memory usage by 6%. + * yes | head -99999999 | sort + * goes down from 1900Mb to 380 Mb. + */ + len = strlen(line); + if (len <= prev_len) { + new_line = prev_line + (prev_len - len); + if (strcmp(line, new_line) == 0) { + /* it's a tail of the prev line */ + if (can_drop_dups && prev_len == len) { + /* it's identical to prev line */ + free(line); + continue; + } + free(line); + line = new_line; + /* continue using longer prev_line + * for future tail tests. + */ + goto skip; + } + } + prev_len = len; + prev_line = line; + skip: ; + } +#else +//TODO: lighter version which only drops total dups if can_drop_dups == true +#endif + lines = xrealloc_vector(lines, 6, linecount); + lines[linecount++] = line; + } + fclose_if_not_stdin(fp); + } while (*++argv); + +#if ENABLE_FEATURE_SORT_BIG + /* If no key, perform alphabetic sort */ + if (!key_list) + add_key()->range[0] = 1; + /* Handle -c */ + if (option_mask32 & FLAG_c) { + int j = (option_mask32 & FLAG_u) ? -1 : 0; + for (i = 1; i < linecount; i++) { + if (compare_keys(&lines[i-1], &lines[i]) > j) { + fprintf(stderr, "Check line %u\n", i); + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; + } +#endif + + /* For stable sort, store original line position beyond terminating NUL */ + if (option_mask32 & FLAG_s) { + for (i = 0; i < linecount; i++) { + uint32_t *p32; + char *line; + unsigned len; + + line = lines[i]; + len = (strlen(line) + 4) & (~3u); + lines[i] = line = xrealloc(line, len + 4); + p32 = (void*)(line + len); + *p32 = i; + } + /*option_mask32 |= FLAG_no_tie_break;*/ + /* ^^^redundant: if FLAG_s, compare_keys() does no tie break */ + } + + /* Perform the actual sort */ + qsort(lines, linecount, sizeof(lines[0]), compare_keys); + + /* Handle -u */ + if (option_mask32 & FLAG_u) { + int j = 0; + /* coreutils 6.3 drop lines for which only key is the same + * -- disabling last-resort compare, or else compare_keys() + * will be the same only for completely identical lines. + */ + option_mask32 |= FLAG_no_tie_break; + for (i = 1; i < linecount; i++) { + if (compare_keys(&lines[j], &lines[i]) == 0) + free(lines[i]); + else + lines[++j] = lines[i]; + } + if (linecount) + linecount = j+1; + } + + /* Print it */ +#if ENABLE_FEATURE_SORT_BIG + /* Open output file _after_ we read all input ones */ + if (option_mask32 & FLAG_o) + xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), STDOUT_FILENO); +#endif + { + 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/busybox/coreutils/split.c b/busybox/coreutils/split.c new file mode 100644 index 00000000000000..4e1db190c1b0e2 --- /dev/null +++ b/busybox/coreutils/split.c @@ -0,0 +1,179 @@ +/* vi: set sw=4 ts=4: */ +/* + * split - split a file into pieces + * Copyright (c) 2007 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config SPLIT +//config: bool "split (5.4 kb)" +//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 + */ + +//usage:#define split_trivial_usage +//usage: "[OPTIONS] [INPUT [PREFIX]]" +//usage:#define split_full_usage "\n\n" +//usage: " -b N[k|m] Split by N (kilo|mega)bytes" +//usage: "\n -l N Split by N lines" +//usage: "\n -a N Use N letters as suffix" +//usage: +//usage:#define split_example_usage +//usage: "$ split TODO foo\n" +//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[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1024*1024 }, + { "g", 1024*1024*1024 }, + { "", 0 } +}; +#endif + +/* Increment the suffix part of the filename. + * Returns NULL if we are out of filenames. + */ +static char *next_file(char *old, unsigned suffix_len) +{ + size_t end = strlen(old); + unsigned i = 1; + char *curr; + + while (1) { + curr = old + end - i; + if (*curr < 'z') { + *curr += 1; + break; + } + i++; + if (i > suffix_len) { + return NULL; + } + *curr = 'a'; + } + + return old; +} + +#define read_buffer bb_common_bufsiz1 +enum { READ_BUFFER_SIZE = COMMON_BUFSIZE - 1 }; + +#define SPLIT_OPT_l (1<<0) +#define SPLIT_OPT_b (1<<1) +#define SPLIT_OPT_a (1<<2) + +int split_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int split_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned suffix_len = 2; + char *pfx; + char *count_p; + const char *sfx; + off_t cnt = 1000; + off_t remaining = 0; + unsigned opt; + ssize_t bytes_read, to_write; + char *src; + + setup_common_bufsiz(); + + opt = getopt32(argv, "^" + "l:b:a:+" /* -a N */ + "\0" "?2"/*max 2 args*/, + &count_p, &count_p, &suffix_len + ); + + if (opt & SPLIT_OPT_l) + cnt = XATOOFF(count_p); + if (opt & SPLIT_OPT_b) // FIXME: also needs XATOOFF + cnt = xatoull_sfx(count_p, + IF_FEATURE_SPLIT_FANCY(split_suffixes) + IF_NOT_FEATURE_SPLIT_FANCY(km_suffixes) + ); + sfx = "x"; + + argv += optind; + if (argv[0]) { + int fd; + if (argv[1]) + sfx = argv[1]; + fd = xopen_stdin(argv[0]); + xmove_fd(fd, STDIN_FILENO); + } else { + argv[0] = (char *) bb_msg_standard_input; + } + + if (NAME_MAX < strlen(sfx) + suffix_len) + bb_error_msg_and_die("suffix too long"); + + { + char *char_p = xzalloc(suffix_len + 1); + memset(char_p, 'a', suffix_len); + pfx = xasprintf("%s%s", sfx, char_p); + if (ENABLE_FEATURE_CLEAN_UP) + free(char_p); + } + + while (1) { + bytes_read = safe_read(STDIN_FILENO, read_buffer, READ_BUFFER_SIZE); + if (!bytes_read) + break; + if (bytes_read < 0) + bb_simple_perror_msg_and_die(argv[0]); + src = read_buffer; + do { + if (!remaining) { + if (!pfx) + bb_error_msg_and_die("suffixes exhausted"); + xmove_fd(xopen(pfx, O_WRONLY | O_CREAT | O_TRUNC), 1); + pfx = next_file(pfx, suffix_len); + remaining = cnt; + } + + if (opt & SPLIT_OPT_b) { + /* split by bytes */ + to_write = (bytes_read < remaining) ? bytes_read : remaining; + remaining -= to_write; + } else { + /* split by lines */ + /* can be sped up by using _memrchr_ + * and writing many lines at once... */ + char *end = memchr(src, '\n', bytes_read); + if (end) { + --remaining; + to_write = end - src + 1; + } else { + to_write = bytes_read; + } + } + + xwrite(STDOUT_FILENO, src, to_write); + bytes_read -= to_write; + src += to_write; + } while (bytes_read); + } + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/stat.c b/busybox/coreutils/stat.c new file mode 100644 index 00000000000000..3d527ae63d5b1c --- /dev/null +++ b/busybox/coreutils/stat.c @@ -0,0 +1,791 @@ +/* vi: set sw=4 ts=4: */ +/* + * stat -- display file or file system status + * + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation. + * Copyright (C) 2005 by Erik Andersen + * Copyright (C) 2005 by Mike Frysinger + * Copyright (C) 2006 by Yoshinori Sato + * + * Written by Michael Meskes + * Taken from coreutils and turned into a busybox applet by Mike Frysinger + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config STAT +//config: bool "stat (10 kb)" +//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_NOEXEC(stat, stat, BB_DIR_BIN, BB_SUID_DROP, stat)) + +//kbuild:lib-$(CONFIG_STAT) += stat.o + +//usage:#define stat_trivial_usage +//usage: "[OPTIONS] FILE..." +//usage:#define stat_full_usage "\n\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: ) +//usage: IF_FEATURE_STAT_FILESYSTEM( +//usage: "\n -f Display filesystem status" +//usage: ) +//usage: "\n -L Follow links" +//usage: "\n -t Terse display" +//usage: IF_SELINUX( +//usage: "\n -Z Print security context" +//usage: ) +//usage: IF_FEATURE_STAT_FORMAT( +//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 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\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: " %t Major device type in hex\n" +//usage: " %T Minor device type in hex\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: 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\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: ) +//usage: " %i File System ID in hex\n" +//usage: " %l Maximum length of filenames\n" +//usage: " %n File name\n" +//usage: " %s Block size (for faster transfer)\n" +//usage: " %S Fundamental block size (for block counts)\n" +//usage: " %t Type in hex\n" +//usage: " %T Type in human readable form" +//usage: ) +//usage: ) + +#include "libbb.h" +#include "common_bufsiz.h" + +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 *); +#else +typedef bool (*statfunc_ptr)(const char *); +#endif + +static const char *file_type(const struct stat *st) +{ + /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107 + * for some of these formats. + * To keep diagnostics grammatical in English, the + * returned string must start with a consonant. + */ + if (S_ISREG(st->st_mode)) return st->st_size == 0 ? "regular empty file" : "regular file"; + if (S_ISDIR(st->st_mode)) return "directory"; + if (S_ISBLK(st->st_mode)) return "block special file"; + if (S_ISCHR(st->st_mode)) return "character special file"; + if (S_ISFIFO(st->st_mode)) return "fifo"; + if (S_ISLNK(st->st_mode)) return "symbolic link"; + if (S_ISSOCK(st->st_mode)) return "socket"; +#ifdef S_TYPEISMQ + if (S_TYPEISMQ(st)) return "message queue"; +#endif +#ifdef S_TYPEISSEM + if (S_TYPEISSEM(st)) return "semaphore"; +#endif +#ifdef S_TYPEISSHM + if (S_TYPEISSHM(st)) return "shared memory object"; +#endif +#ifdef S_TYPEISTMO + if (S_TYPEISTMO(st)) return "typed memory object"; +#endif + return "weird file"; +} + +static const char *human_time(time_t t) +{ + /* Old + static char *str; + str = ctime(&t); + str[strlen(str)-1] = '\0'; + return str; + */ + /* coreutils 6.3 compat: */ + + /*static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")] ALIGN1;*/ +#define buf bb_common_bufsiz1 + 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) + * Still others have neither and have to get by with f_type (Linux). + */ +static const char *human_fstype(uint32_t f_type) +{ + static const struct types { + uint32_t type; + const char *const fs; + } humantypes[] = { + { 0xADFF, "affs" }, + { 0x1Cd1, "devpts" }, + { 0x137D, "ext" }, + { 0xEF51, "ext2" }, + { 0xEF53, "ext2/ext3" }, + { 0x3153464a, "jfs" }, + { 0x58465342, "xfs" }, + { 0xF995E849, "hpfs" }, + { 0x9660, "isofs" }, + { 0x4000, "isofs" }, + { 0x4004, "isofs" }, + { 0x137F, "minix" }, + { 0x138F, "minix (30 char.)" }, + { 0x2468, "minix v2" }, + { 0x2478, "minix v2 (30 char.)" }, + { 0x4d44, "msdos" }, + { 0x4006, "fat" }, + { 0x564c, "novell" }, + { 0x6969, "nfs" }, + { 0x9fa0, "proc" }, + { 0x517B, "smb" }, + { 0x012FF7B4, "xenix" }, + { 0x012FF7B5, "sysv4" }, + { 0x012FF7B6, "sysv2" }, + { 0x012FF7B7, "coh" }, + { 0x00011954, "ufs" }, + { 0x012FD16D, "xia" }, + { 0x5346544e, "ntfs" }, + { 0x1021994, "tmpfs" }, + { 0x52654973, "reiserfs" }, + { 0x28cd3d45, "cramfs" }, + { 0x7275, "romfs" }, + { 0x858458f6, "ramfs" }, + { 0x73717368, "squashfs" }, + { 0x62656572, "sysfs" }, + { 0, "UNKNOWN" } + }; + + int i; + + for (i = 0; humantypes[i].type; ++i) + if (humantypes[i].type == f_type) + break; + return humantypes[i].fs; +} + +/* "man statfs" says that statfsbuf->f_fsid is a mess */ +/* coreutils treats it as an array of ints, most significant first */ +static unsigned long long get_f_fsid(const struct statfs *statfsbuf) +{ + const unsigned *p = (const void*) &statfsbuf->f_fsid; + unsigned sz = sizeof(statfsbuf->f_fsid) / sizeof(unsigned); + unsigned long long r = 0; + + do + r = (r << (sizeof(unsigned)*8)) | *p++; + while (--sz > 0); + return r; +} +#endif /* FEATURE_STAT_FILESYSTEM */ + +#if ENABLE_FEATURE_STAT_FORMAT +static void strcatc(char *str, char c) +{ + int len = strlen(str); + str[len++] = c; + str[len] = '\0'; +} + +static void printfs(char *pformat, const char *msg) +{ + strcatc(pformat, 's'); + 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 + IF_SELINUX(, security_context_t scontext)) +{ + const struct statfs *statfsbuf = data; + if (m == 'n') { + printfs(pformat, filename); + } else if (m == 'i') { + strcat(pformat, "llx"); + printf(pformat, get_f_fsid(statfsbuf)); + } else if (m == 'l') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statfsbuf->f_namelen); + } else if (m == 't') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) statfsbuf->f_type); /* no equiv */ + } else if (m == 'T') { + printfs(pformat, human_fstype(statfsbuf->f_type)); + } else if (m == 'b') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statfsbuf->f_blocks); + } else if (m == 'f') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statfsbuf->f_bfree); + } else if (m == 'a') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statfsbuf->f_bavail); + } else if (m == 's' || m == 'S') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statfsbuf->f_bsize); + } else if (m == 'c') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statfsbuf->f_files); + } else if (m == 'd') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statfsbuf->f_ffree); +# if ENABLE_SELINUX + } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) { + printfs(pformat, scontext); +# endif + } else { + strcatc(pformat, 'c'); + printf(pformat, m); + } +} +#endif + +/* print stat info */ +static void FAST_FUNC print_stat(char *pformat, const char m, + const char *const filename, const void *data + IF_SELINUX(, security_context_t scontext)) +{ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + struct stat *statbuf = (struct stat *) data; + struct passwd *pw_ent; + struct group *gw_ent; + + if (m == 'n') { + printfs(pformat, filename); + } else if (m == 'N') { + strcatc(pformat, 's'); + if (S_ISLNK(statbuf->st_mode)) { + char *linkname = xmalloc_readlink_or_warn(filename); + if (linkname == NULL) + return; + printf("'%s' -> '%s'", filename, linkname); + free(linkname); + } else { + printf(pformat, filename); + } + } else if (m == 'd') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statbuf->st_dev); + } else if (m == 'D') { + strcat(pformat, "llx"); + printf(pformat, (unsigned long long) statbuf->st_dev); + } else if (m == 'i') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statbuf->st_ino); + } else if (m == 'a') { + strcat(pformat, "lo"); + printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO))); + } else if (m == 'A') { + printfs(pformat, bb_mode_string(statbuf->st_mode)); + } else if (m == 'f') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) statbuf->st_mode); + } else if (m == 'F') { + printfs(pformat, file_type(statbuf)); + } else if (m == 'h') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_nlink); + } else if (m == 'u') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_uid); + } else if (m == 'U') { + pw_ent = getpwuid(statbuf->st_uid); + printfs(pformat, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN"); + } else if (m == 'g') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_gid); + } else if (m == 'G') { + gw_ent = getgrgid(statbuf->st_gid); + printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); + } else if (m == 't') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) major(statbuf->st_rdev)); + } else if (m == 'T') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) minor(statbuf->st_rdev)); + } else if (m == 's') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statbuf->st_size); + } else if (m == 'B') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) 512); //ST_NBLOCKSIZE + } else if (m == 'b') { + strcat(pformat, "llu"); + printf(pformat, (unsigned long long) statbuf->st_blocks); + } else if (m == 'o') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_blksize); + } else if (m == 'x') { + printfs(pformat, human_time(statbuf->st_atime)); + } else if (m == 'X') { + strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); + /* note: (unsigned long) would be wrong: + * imagine (unsigned long64)int32 */ + printf(pformat, (long) statbuf->st_atime); + } else if (m == 'y') { + printfs(pformat, human_time(statbuf->st_mtime)); + } else if (m == 'Y') { + strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); + printf(pformat, (long) statbuf->st_mtime); + } else if (m == 'z') { + printfs(pformat, human_time(statbuf->st_ctime)); + } else if (m == 'Z') { + strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); + printf(pformat, (long) statbuf->st_ctime); +# if ENABLE_SELINUX + } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) { + printfs(pformat, scontext); +# endif + } else { + strcatc(pformat, 'c'); + printf(pformat, m); + } +} + +static void print_it(const char *masterformat, + const char *filename, + void FAST_FUNC (*print_func)(char*, char, const char*, const void* IF_SELINUX(, security_context_t scontext)), + const void *data + IF_SELINUX(, security_context_t scontext)) +{ + /* Create a working copy of the format string */ + char *format = xstrdup(masterformat); + /* 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; + + b = format; + while (b) { + /* Each iteration finds next %spec, + * prints preceding string and handles found %spec + */ + size_t len; + char *p = strchr(b, '%'); + if (!p) { + /* coreutils 6.3 always prints newline at the end */ + /*fputs(b, stdout);*/ + puts(b); + break; + } + + /* dest = "%" */ + len = 1 + strspn(p + 1, "#-+.I 0123456789"); + memcpy(dest, p, len); + dest[len] = '\0'; + + /* print preceding string */ + *p = '\0'; + fputs(b, stdout); + + p += len; + b = p + 1; + switch (*p) { + case '\0': + b = NULL; + /* fall through */ + case '%': + bb_putchar('%'); + break; + default: + /* Completes "%" with specifier and printfs */ + print_func(dest, *p, filename, data IF_SELINUX(,scontext)); + break; + } + } + + free(format); + free(dest); +} +#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) +#endif +static bool do_statfs(const char *filename, const char *format) +{ + struct statfs statfsbuf; +#if !ENABLE_FEATURE_STAT_FORMAT + const char *format; +#endif +#if ENABLE_SELINUX + security_context_t scontext = NULL; + + if (option_mask32 & OPT_SELINUX) { + if ((option_mask32 & OPT_DEREFERENCE + ? lgetfilecon(filename, &scontext) + : getfilecon(filename, &scontext) + ) < 0 + ) { + bb_simple_perror_msg(filename); + return 0; + } + } +#endif + if (statfs(filename, &statfsbuf) != 0) { + bb_perror_msg("can't read file system information for '%s'", filename); + return 0; + } + +#if ENABLE_FEATURE_STAT_FORMAT + if (format == NULL) { +# if !ENABLE_SELINUX + format = (option_mask32 & OPT_TERSE + ? "%n %i %l %t %s %b %f %a %c %d\n" + : " File: \"%n\"\n" + " ID: %-8i Namelen: %-7l Type: %T\n" + "Block size: %-10s\n" + "Blocks: Total: %-10b Free: %-10f Available: %a\n" + "Inodes: Total: %-10c Free: %d"); +# else + format = (option_mask32 & OPT_TERSE + ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n": + "%n %i %l %t %s %b %f %a %c %d\n") + : (option_mask32 & OPT_SELINUX ? + " File: \"%n\"\n" + " ID: %-8i Namelen: %-7l Type: %T\n" + "Block size: %-10s\n" + "Blocks: Total: %-10b Free: %-10f Available: %a\n" + "Inodes: Total: %-10c Free: %d" + " S_context: %C\n": + " File: \"%n\"\n" + " ID: %-8i Namelen: %-7l Type: %T\n" + "Block size: %-10s\n" + "Blocks: Total: %-10b Free: %-10f Available: %a\n" + "Inodes: Total: %-10c Free: %d\n") + ); +# endif /* SELINUX */ + } + print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext)); +#else /* FEATURE_STAT_FORMAT */ + format = (option_mask32 & OPT_TERSE + ? "%s %llx %lu " + : " File: \"%s\"\n" + " ID: %-8llx Namelen: %-7lu "); + printf(format, + filename, + get_f_fsid(&statfsbuf), + statfsbuf.f_namelen); + + if (option_mask32 & OPT_TERSE) + printf("%lx ", (unsigned long) statfsbuf.f_type); + else + printf("Type: %s\n", human_fstype(statfsbuf.f_type)); + +# if !ENABLE_SELINUX + format = (option_mask32 & OPT_TERSE + ? "%lu %llu %llu %llu %llu %llu\n" + : "Block size: %-10lu\n" + "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n" + "Inodes: Total: %-10llu Free: %llu\n"); + printf(format, + (unsigned long) statfsbuf.f_bsize, + (unsigned long long) statfsbuf.f_blocks, + (unsigned long long) statfsbuf.f_bfree, + (unsigned long long) statfsbuf.f_bavail, + (unsigned long long) statfsbuf.f_files, + (unsigned long long) statfsbuf.f_ffree); +# else + format = (option_mask32 & OPT_TERSE + ? (option_mask32 & OPT_SELINUX ? "%lu %llu %llu %llu %llu %llu %C\n" : "%lu %llu %llu %llu %llu %llu\n") + : (option_mask32 & OPT_SELINUX + ? "Block size: %-10lu\n" + "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n" + "Inodes: Total: %-10llu Free: %llu" + "S_context: %C\n" + : "Block size: %-10lu\n" + "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n" + "Inodes: Total: %-10llu Free: %llu\n" + ) + ); + printf(format, + (unsigned long) statfsbuf.f_bsize, + (unsigned long long) statfsbuf.f_blocks, + (unsigned long long) statfsbuf.f_bfree, + (unsigned long long) statfsbuf.f_bavail, + (unsigned long long) statfsbuf.f_files, + (unsigned long long) statfsbuf.f_ffree, + scontext); + + if (scontext) + freecon(scontext); +# endif +#endif /* FEATURE_STAT_FORMAT */ + return 1; +} +#endif /* FEATURE_STAT_FILESYSTEM */ + +/* stat the file and print what we find */ +#if !ENABLE_FEATURE_STAT_FORMAT +#define do_stat(filename, format) do_stat(filename) +#endif +static bool do_stat(const char *filename, const char *format) +{ + struct stat statbuf; +#if ENABLE_SELINUX + security_context_t scontext = NULL; + + if (option_mask32 & OPT_SELINUX) { + if ((option_mask32 & OPT_DEREFERENCE + ? lgetfilecon(filename, &scontext) + : getfilecon(filename, &scontext) + ) < 0 + ) { + bb_simple_perror_msg(filename); + return 0; + } + } +#endif + if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) { + bb_perror_msg("can't stat '%s'", filename); + return 0; + } + +#if ENABLE_FEATURE_STAT_FORMAT + if (format == NULL) { +# if !ENABLE_SELINUX + if (option_mask32 & OPT_TERSE) { + format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; + } else { + if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { + format = + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" + " Device type: %t,%T\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"; + } else { + format = + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"; + } + } +# else + if (option_mask32 & OPT_TERSE) { + format = (option_mask32 & OPT_SELINUX ? + "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n" + : + "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n" + ); + } else { + if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { + format = (option_mask32 & OPT_SELINUX ? + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" + " Device type: %t,%T\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + " S_Context: %C\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n" + : + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" + " Device type: %t,%T\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n" + ); + } else { + format = (option_mask32 & OPT_SELINUX ? + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "S_Context: %C\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n" + : + " File: %N\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n" + ); + } + } +# endif + } + print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext)); +#else /* FEATURE_STAT_FORMAT */ + if (option_mask32 & OPT_TERSE) { + printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu" + IF_NOT_SELINUX("\n"), + filename, + (unsigned long long) statbuf.st_size, + (unsigned long long) statbuf.st_blocks, + (unsigned long) statbuf.st_mode, + (unsigned long) statbuf.st_uid, + (unsigned long) statbuf.st_gid, + (unsigned long long) statbuf.st_dev, + (unsigned long long) statbuf.st_ino, + (unsigned long) statbuf.st_nlink, + (unsigned long) major(statbuf.st_rdev), + (unsigned long) minor(statbuf.st_rdev), + (unsigned long) statbuf.st_atime, + (unsigned long) statbuf.st_mtime, + (unsigned long) statbuf.st_ctime, + (unsigned long) statbuf.st_blksize + ); +# if ENABLE_SELINUX + if (option_mask32 & OPT_SELINUX) + printf(" %s\n", scontext); + else + bb_putchar('\n'); +# endif + } else { + char *linkname = NULL; + struct passwd *pw_ent; + struct group *gw_ent; + + gw_ent = getgrgid(statbuf.st_gid); + pw_ent = getpwuid(statbuf.st_uid); + + if (S_ISLNK(statbuf.st_mode)) + linkname = xmalloc_readlink_or_warn(filename); + if (linkname) { + printf(" File: '%s' -> '%s'\n", filename, linkname); + free(linkname); + } else { + printf(" File: '%s'\n", filename); + } + + printf(" Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n" + "Device: %llxh/%llud\tInode: %-10llu Links: %-5lu", + (unsigned long long) statbuf.st_size, + (unsigned long long) statbuf.st_blocks, + (unsigned long) statbuf.st_blksize, + file_type(&statbuf), + (unsigned long long) statbuf.st_dev, + (unsigned long long) statbuf.st_dev, + (unsigned long long) statbuf.st_ino, + (unsigned long) statbuf.st_nlink); + if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) + printf(" Device type: %lx,%lx\n", + (unsigned long) major(statbuf.st_rdev), + (unsigned long) minor(statbuf.st_rdev)); + else + bb_putchar('\n'); + printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n", + (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)), + bb_mode_string(statbuf.st_mode), + (unsigned long) statbuf.st_uid, + (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN", + (unsigned long) statbuf.st_gid, + (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); +# if ENABLE_SELINUX + if (option_mask32 & OPT_SELINUX) + printf(" S_Context: %s\n", scontext); +# endif + printf("Access: %s\n", human_time(statbuf.st_atime)); + printf("Modify: %s\n", human_time(statbuf.st_mtime)); + printf("Change: %s\n", human_time(statbuf.st_ctime)); + } +#endif /* FEATURE_STAT_FORMAT */ + return 1; +} + +int stat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int stat_main(int argc UNUSED_PARAM, char **argv) +{ + IF_FEATURE_STAT_FORMAT(char *format = NULL;) + int i; + int ok; + statfunc_ptr statfunc = do_stat; +#if ENABLE_FEATURE_STAT_FILESYSTEM || ENABLE_SELINUX + unsigned opts; + + opts = +#endif + getopt32(argv, "^" + "tL" + IF_FEATURE_STAT_FILESYSTEM("f") + IF_SELINUX("Z") + IF_FEATURE_STAT_FORMAT("c:") + "\0" "-1" /* min one arg */ + IF_FEATURE_STAT_FORMAT(,&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(); + } +#endif + ok = 1; + argv += optind; + for (i = 0; argv[i]; ++i) + ok &= statfunc(argv[i] IF_FEATURE_STAT_FORMAT(, format)); + + return (ok ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/busybox/coreutils/stty.c b/busybox/coreutils/stty.c new file mode 100644 index 00000000000000..424d909cf3c40a --- /dev/null +++ b/busybox/coreutils/stty.c @@ -0,0 +1,1566 @@ +/* vi: set sw=4 ts=4: */ +/* + * stty -- change and print terminal line settings + * Copyright (C) 1990-1999 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Usage: stty [-ag] [-F device] [setting...] + * + * Options: + * -a Write all current settings to stdout in human-readable form. + * -g Write all current settings to stdout in stty-readable form. + * -F Open and use the specified device instead of stdin + * + * If no args are given, write to stdout the baud rate and settings that + * have been changed from their defaults. Mode reading and changes + * are done on the specified device, or stdin if none was specified. + * + * David MacKenzie + * + * Special for busybox ported by Vladimir Oleynik 2001 + */ +//config:config STTY +//config: bool "stty (8.6 kb)" +//config: default y +//config: help +//config: stty is used to change and print terminal line settings. + +//applet:IF_STTY(APPLET_NOEXEC(stty, stty, BB_DIR_BIN, BB_SUID_DROP, stty)) + +//kbuild:lib-$(CONFIG_STTY) += stty.o + +//usage:#define stty_trivial_usage +//usage: "[-a|g] [-F DEVICE] [SETTING]..." +//usage:#define stty_full_usage "\n\n" +//usage: "Without arguments, prints baud rate, line discipline,\n" +//usage: "and deviations from stty sane\n" +//usage: "\n -F DEVICE Open device instead of stdin" +//usage: "\n -a Print all current settings in human-readable form" +//usage: "\n -g Print in stty-readable form" +//usage: "\n [SETTING] See manpage" + +#include "libbb.h" +#include "common_bufsiz.h" + +#ifndef _POSIX_VDISABLE +# define _POSIX_VDISABLE ((unsigned char) 0) +#endif + +#define Control(c) ((c) & 0x1f) +/* Canonical values for control characters */ +#ifndef CINTR +# define CINTR Control('c') +#endif +#ifndef CQUIT +# define CQUIT 28 +#endif +#ifndef CERASE +# define CERASE 127 +#endif +#ifndef CKILL +# define CKILL Control('u') +#endif +#ifndef CEOF +# define CEOF Control('d') +#endif +#ifndef CEOL +# define CEOL _POSIX_VDISABLE +#endif +#ifndef CSTART +# define CSTART Control('q') +#endif +#ifndef CSTOP +# define CSTOP Control('s') +#endif +#ifndef CSUSP +# define CSUSP Control('z') +#endif +#if defined(VEOL2) && !defined(CEOL2) +# define CEOL2 _POSIX_VDISABLE +#endif +/* glibc-2.12.1 uses only VSWTC name */ +#if defined(VSWTC) && !defined(VSWTCH) +# define VSWTCH VSWTC +#endif +/* ISC renamed swtch to susp for termios, but we'll accept either name */ +#if defined(VSUSP) && !defined(VSWTCH) +# define VSWTCH VSUSP +# define CSWTCH CSUSP +#endif +#if defined(VSWTCH) && !defined(CSWTCH) +# define CSWTCH _POSIX_VDISABLE +#endif + +/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'. + So the default is to disable 'swtch.' */ +#if defined(__sparc__) && defined(__svr4__) +# undef CSWTCH +# define CSWTCH _POSIX_VDISABLE +#endif + +#if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */ +# define VWERASE VWERSE +#endif +#if defined(VDSUSP) && !defined(CDSUSP) +# define CDSUSP Control('y') +#endif +#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */ +# define VREPRINT VRPRNT +#endif +#if defined(VREPRINT) && !defined(CRPRNT) +# define CRPRNT Control('r') +#endif +#if defined(VWERASE) && !defined(CWERASE) +# define CWERASE Control('w') +#endif +#if defined(VLNEXT) && !defined(CLNEXT) +# define CLNEXT Control('v') +#endif +#if defined(VDISCARD) && !defined(VFLUSHO) +# define VFLUSHO VDISCARD +#endif +#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */ +# define VFLUSHO VFLUSH +#endif +#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */ +# define ECHOCTL CTLECH +#endif +#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */ +# define ECHOCTL TCTLECH +#endif +#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */ +# define ECHOKE CRTKIL +#endif +#if defined(VFLUSHO) && !defined(CFLUSHO) +# define CFLUSHO Control('o') +#endif +#if defined(VSTATUS) && !defined(CSTATUS) +# define CSTATUS Control('t') +#endif + +/* Save us from #ifdef forest plague */ +#ifndef BSDLY +# define BSDLY 0 +#endif +#ifndef CIBAUD +# define CIBAUD 0 +#endif +#ifndef CRDLY +# define CRDLY 0 +#endif +#ifndef CMSPAR +# define CMSPAR 0 +#endif +#ifndef CRTSCTS +# define CRTSCTS 0 +#endif +#ifndef ECHOCTL +# define ECHOCTL 0 +#endif +#ifndef ECHOKE +# define ECHOKE 0 +#endif +#ifndef ECHOPRT +# define ECHOPRT 0 +#endif +#ifndef FFDLY +# define FFDLY 0 +#endif +#ifndef IEXTEN +# define IEXTEN 0 +#endif +#ifndef IMAXBEL +# define IMAXBEL 0 +#endif +#ifndef IUCLC +# define IUCLC 0 +#endif +#ifndef IXANY +# define IXANY 0 +#endif +#ifndef NLDLY +# define NLDLY 0 +#endif +#ifndef OCRNL +# define OCRNL 0 +#endif +#ifndef OFDEL +# define OFDEL 0 +#endif +#ifndef OFILL +# define OFILL 0 +#endif +#ifndef OLCUC +# define OLCUC 0 +#endif +#ifndef ONLCR +# define ONLCR 0 +#endif +#ifndef ONLRET +# define ONLRET 0 +#endif +#ifndef ONOCR +# define ONOCR 0 +#endif +#ifndef OXTABS +# define OXTABS 0 +#endif +#ifndef TABDLY +# define TABDLY 0 +#endif +#ifndef TAB1 +# define TAB1 0 +#endif +#ifndef TAB2 +# define TAB2 0 +#endif +#ifndef TOSTOP +# define TOSTOP 0 +#endif +#ifndef VDSUSP +# define VDSUSP 0 +#endif +#ifndef VEOL2 +# define VEOL2 0 +#endif +#ifndef VFLUSHO +# define VFLUSHO 0 +#endif +#ifndef VLNEXT +# define VLNEXT 0 +#endif +#ifndef VREPRINT +# define VREPRINT 0 +#endif +#ifndef VSTATUS +# define VSTATUS 0 +#endif +#ifndef VSWTCH +# define VSWTCH 0 +#endif +#ifndef VTDLY +# define VTDLY 0 +#endif +#ifndef VWERASE +# define VWERASE 0 +#endif +#ifndef XCASE +# define XCASE 0 +#endif +#ifndef IUTF8 +# define IUTF8 0 +#endif + +/* Which speeds to set */ +enum speed_setting { + input_speed, output_speed, both_speeds +}; + +/* Which member(s) of 'struct termios' a mode uses */ +enum { + control, input, output, local, combination +}; +static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode) +{ + static const uint8_t tcflag_offsets[] ALIGN1 = { + offsetof(struct termios, c_cflag), /* control */ + offsetof(struct termios, c_iflag), /* input */ + offsetof(struct termios, c_oflag), /* output */ + offsetof(struct termios, c_lflag) /* local */ + }; + if (type <= local) { + return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]); + } + return NULL; +} + +/* Flags for 'struct mode_info' */ +#define SANE_SET 1 /* Set in 'sane' mode */ +#define SANE_UNSET 2 /* Unset in 'sane' mode */ +#define REV 4 /* Can be turned off by prepending '-' */ +#define OMIT 8 /* Don't display value */ + + +/* Each mode. + * This structure should be kept as small as humanly possible. + */ +struct mode_info { + const uint8_t type; /* Which structure element to change */ + const uint8_t flags; /* Setting and display options */ + /* only these values are ever used, so... */ +#if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100 + const uint8_t mask; +#elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000 + const uint16_t mask; +#else + const tcflag_t mask; /* Other bits to turn off for this mode */ +#endif + /* was using short here, but ppc32 was unhappy */ + const tcflag_t bits; /* Bits to set for this mode */ +}; + +enum { + /* Must match mode_name[] and mode_info[] order! */ + IDX_evenp = 0, + IDX_parity, + IDX_oddp, + IDX_nl, + IDX_ek, + IDX_sane, + IDX_cooked, + IDX_raw, + IDX_pass8, + IDX_litout, + IDX_cbreak, + IDX_crt, + IDX_dec, +#if IXANY + IDX_decctlq, +#endif +#if TABDLY || OXTABS + IDX_tabs, +#endif +#if XCASE && IUCLC && OLCUC + IDX_lcase, + IDX_LCASE, +#endif +}; + +#define MI_ENTRY(N,T,F,B,M) N "\0" + +/* Mode names given on command line */ +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 ) + MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("ek", combination, OMIT, 0, 0 ) + MI_ENTRY("sane", combination, OMIT, 0, 0 ) + MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("crt", combination, OMIT, 0, 0 ) + MI_ENTRY("dec", combination, OMIT, 0, 0 ) +#if IXANY + MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) +#endif +#if TABDLY || OXTABS + MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) +#endif +#if XCASE && IUCLC && OLCUC + MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) +#endif + MI_ENTRY("parenb", control, REV, PARENB, 0 ) + MI_ENTRY("parodd", control, REV, PARODD, 0 ) +#if CMSPAR + MI_ENTRY("cmspar", control, REV, CMSPAR, 0 ) +#endif + MI_ENTRY("cs5", control, 0, CS5, CSIZE) + MI_ENTRY("cs6", control, 0, CS6, CSIZE) + MI_ENTRY("cs7", control, 0, CS7, CSIZE) + MI_ENTRY("cs8", control, 0, CS8, CSIZE) + MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) + MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) + MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) + MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) + MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) +#if CRTSCTS + MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) +#endif + MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) + MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) + MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) + MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) + MI_ENTRY("inpck", input, REV, INPCK, 0 ) + MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) + MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) + MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) + MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) + MI_ENTRY("ixon", input, REV, IXON, 0 ) + MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) + MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) +#if IUCLC + MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) +#endif +#if IXANY + MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) +#endif +#if IMAXBEL + MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) +#endif +#if IUTF8 + MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) +#endif + MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) +#if OLCUC + MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) +#endif +#if OCRNL + MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) +#endif +#if ONLCR + MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) +#endif +#if ONOCR + MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) +#endif +#if ONLRET + MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) +#endif +#if OFILL + MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) +#endif +#if OFDEL + MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) +#endif +#if NLDLY + MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) + MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) +#endif +#if CRDLY + MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) + MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) + MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) + MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) +#endif + +#if TABDLY + MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) +# if TAB2 + MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) +# endif +# if TAB1 + MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) +# endif + MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) +#else +# if OXTABS + MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) +# endif +#endif + +#if BSDLY + MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) + MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) +#endif +#if VTDLY + MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) + MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) +#endif +#if FFDLY + MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) + MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) +#endif + MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) + MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) +#if IEXTEN + MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) +#endif + MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) + MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) + MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) + MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) + MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) + MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) +#if XCASE + MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) +#endif +#if TOSTOP + MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) +#endif +#if ECHOPRT + MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) + MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) +#endif +#if ECHOCTL + MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) + MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) +#endif +#if ECHOKE + MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) + MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) +#endif + MI_ENTRY("flusho", local, SANE_UNSET | REV, FLUSHO, 0 ) +#ifdef EXTPROC + MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 ) +#endif + ; + +#undef MI_ENTRY +#define MI_ENTRY(N,T,F,B,M) { T, F, M, B }, + +static const struct mode_info mode_info[] = { + /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */ + MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("ek", combination, OMIT, 0, 0 ) + MI_ENTRY("sane", combination, OMIT, 0, 0 ) + MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("crt", combination, OMIT, 0, 0 ) + MI_ENTRY("dec", combination, OMIT, 0, 0 ) +#if IXANY + MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) +#endif +#if TABDLY || OXTABS + MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) +#endif +#if XCASE && IUCLC && OLCUC + MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) +#endif + MI_ENTRY("parenb", control, REV, PARENB, 0 ) + MI_ENTRY("parodd", control, REV, PARODD, 0 ) +#if CMSPAR + MI_ENTRY("cmspar", control, REV, CMSPAR, 0 ) +#endif + MI_ENTRY("cs5", control, 0, CS5, CSIZE) + MI_ENTRY("cs6", control, 0, CS6, CSIZE) + MI_ENTRY("cs7", control, 0, CS7, CSIZE) + MI_ENTRY("cs8", control, 0, CS8, CSIZE) + MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) + MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) + MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) + MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) + MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) +#if CRTSCTS + MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) +#endif + MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) + MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) + MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) + MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) + MI_ENTRY("inpck", input, REV, INPCK, 0 ) + MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) + MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) + MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) + MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) + MI_ENTRY("ixon", input, REV, IXON, 0 ) + MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) + MI_ENTRY("tandem", input, OMIT | REV, IXOFF, 0 ) +#if IUCLC + MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) +#endif +#if IXANY + MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) +#endif +#if IMAXBEL + MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) +#endif +#if IUTF8 + MI_ENTRY("iutf8", input, SANE_UNSET | REV, IUTF8, 0 ) +#endif + MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) +#if OLCUC + MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) +#endif +#if OCRNL + MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) +#endif +#if ONLCR + MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) +#endif +#if ONOCR + MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) +#endif +#if ONLRET + MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) +#endif +#if OFILL + MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) +#endif +#if OFDEL + MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) +#endif +#if NLDLY + MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) + MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) +#endif +#if CRDLY + MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) + MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) + MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) + MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) +#endif + +#if TABDLY + MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) +# if TAB2 + MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) +# endif +# if TAB1 + MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) +# endif + MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) +#else +# if OXTABS + MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) +# endif +#endif + +#if BSDLY + MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) + MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) +#endif +#if VTDLY + MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) + MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) +#endif +#if FFDLY + MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) + MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) +#endif + MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) + MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) +#if IEXTEN + MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) +#endif + MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) + MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) + MI_ENTRY("crterase", local, OMIT | REV, ECHOE, 0 ) + MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) + MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) + MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) +#if XCASE + MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) +#endif +#if TOSTOP + MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) +#endif +#if ECHOPRT + MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) + MI_ENTRY("prterase", local, OMIT | REV, ECHOPRT, 0 ) +#endif +#if ECHOCTL + MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) + MI_ENTRY("ctlecho", local, OMIT | REV, ECHOCTL, 0 ) +#endif +#if ECHOKE + MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) + MI_ENTRY("crtkill", local, OMIT | REV, ECHOKE, 0 ) +#endif + MI_ENTRY("flusho", local, SANE_UNSET | REV, FLUSHO, 0 ) +#ifdef EXTPROC + MI_ENTRY("extproc", local, SANE_UNSET | REV, EXTPROC, 0 ) +#endif +}; + +enum { + NUM_mode_info = ARRAY_SIZE(mode_info) +}; + + +/* Control characters */ +struct control_info { + const uint8_t saneval; /* Value to set for 'stty sane' */ + const uint8_t offset; /* Offset in c_cc */ +}; + +enum { + /* Must match control_name[] and control_info[] order! */ + CIDX_intr = 0, + CIDX_quit, + CIDX_erase, + CIDX_kill, + CIDX_eof, + CIDX_eol, +#if VEOL2 + CIDX_eol2, +#endif +#if VSWTCH + CIDX_swtch, +#endif + CIDX_start, + CIDX_stop, + CIDX_susp, +#if VDSUSP + CIDX_dsusp, +#endif +#if VREPRINT + CIDX_rprnt, +#endif +#if VWERASE + CIDX_werase, +#endif +#if VLNEXT + CIDX_lnext, +#endif +#if VFLUSHO + CIDX_flush, +#endif +#if VSTATUS + CIDX_status, +#endif + CIDX_min, + CIDX_time, +}; + +#define CI_ENTRY(n,s,o) n "\0" + +/* Name given on command line */ +static const char control_name[] ALIGN1 = + CI_ENTRY("intr", CINTR, VINTR ) + CI_ENTRY("quit", CQUIT, VQUIT ) + CI_ENTRY("erase", CERASE, VERASE ) + CI_ENTRY("kill", CKILL, VKILL ) + CI_ENTRY("eof", CEOF, VEOF ) + CI_ENTRY("eol", CEOL, VEOL ) +#if VEOL2 + CI_ENTRY("eol2", CEOL2, VEOL2 ) +#endif +#if VSWTCH + CI_ENTRY("swtch", CSWTCH, VSWTCH ) +#endif + CI_ENTRY("start", CSTART, VSTART ) + CI_ENTRY("stop", CSTOP, VSTOP ) + CI_ENTRY("susp", CSUSP, VSUSP ) +#if VDSUSP + CI_ENTRY("dsusp", CDSUSP, VDSUSP ) +#endif +#if VREPRINT + CI_ENTRY("rprnt", CRPRNT, VREPRINT) +#endif +#if VWERASE + CI_ENTRY("werase", CWERASE, VWERASE ) +#endif +#if VLNEXT + CI_ENTRY("lnext", CLNEXT, VLNEXT ) +#endif +#if VFLUSHO + CI_ENTRY("flush", CFLUSHO, VFLUSHO ) +#endif +#if VSTATUS + CI_ENTRY("status", CSTATUS, VSTATUS ) +#endif + /* These must be last because of the display routines */ + CI_ENTRY("min", 1, VMIN ) + CI_ENTRY("time", 0, VTIME ) + ; + +#undef CI_ENTRY +#define CI_ENTRY(n,s,o) { s, o }, + +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 ) + CI_ENTRY("erase", CERASE, VERASE ) + CI_ENTRY("kill", CKILL, VKILL ) + CI_ENTRY("eof", CEOF, VEOF ) + CI_ENTRY("eol", CEOL, VEOL ) +#if VEOL2 + CI_ENTRY("eol2", CEOL2, VEOL2 ) +#endif +#if VSWTCH + CI_ENTRY("swtch", CSWTCH, VSWTCH ) +#endif + CI_ENTRY("start", CSTART, VSTART ) + CI_ENTRY("stop", CSTOP, VSTOP ) + CI_ENTRY("susp", CSUSP, VSUSP ) +#if VDSUSP + CI_ENTRY("dsusp", CDSUSP, VDSUSP ) +#endif +#if VREPRINT + CI_ENTRY("rprnt", CRPRNT, VREPRINT) +#endif +#if VWERASE + CI_ENTRY("werase", CWERASE, VWERASE ) +#endif +#if VLNEXT + CI_ENTRY("lnext", CLNEXT, VLNEXT ) +#endif +#if VFLUSHO + CI_ENTRY("flush", CFLUSHO, VFLUSHO ) +#endif +#if VSTATUS + CI_ENTRY("status", CSTATUS, VSTATUS ) +#endif + /* These must be last because of the display routines */ + CI_ENTRY("min", 1, VMIN ) + CI_ENTRY("time", 0, VTIME ) +}; + +enum { + NUM_control_info = ARRAY_SIZE(control_info) +}; + + +struct globals { + const char *device_name; + /* The width of the screen, for output wrapping */ + unsigned max_col; + /* Current position, to know when to wrap */ + unsigned current_col; +} FIX_ALIASING; +#define G (*(struct globals*)bb_common_bufsiz1) +#define INIT_G() do { \ + setup_common_bufsiz(); \ + G.device_name = bb_msg_standard_input; \ + G.max_col = 80; \ + G.current_col = 0; /* we are noexec, must clear */ \ +} while (0) + +static void set_speed_or_die(enum speed_setting type, const char *arg, + struct termios *mode) +{ + speed_t baud; + + baud = tty_value_to_baud(xatou(arg)); + + if (type != output_speed) { /* either input or both */ + cfsetispeed(mode, baud); + } + if (type != input_speed) { /* either output or both */ + cfsetospeed(mode, baud); + } +} + +static NORETURN void perror_on_device_and_die(const char *fmt) +{ + bb_perror_msg_and_die(fmt, G.device_name); +} + +static void perror_on_device(const char *fmt) +{ + bb_perror_msg(fmt, G.device_name); +} + +/* Print format string MESSAGE and optional args. + Wrap to next line first if it won't fit. + Print a space first unless MESSAGE will start a new line */ +static void wrapf(const char *message, ...) +{ + char buf[128]; + va_list args; + unsigned buflen; + + va_start(args, message); + buflen = vsnprintf(buf, sizeof(buf), message, args); + va_end(args); + /* We seem to be called only with suitable lengths, but check if + somebody failed to adhere to this assumption just to be sure. */ + if (!buflen || buflen >= sizeof(buf)) return; + + if (G.current_col > 0) { + G.current_col++; + if (buf[0] != '\n') { + if (G.current_col + buflen >= G.max_col) { + G.current_col = 0; + bb_putchar('\n'); + } else { + bb_putchar(' '); + } + } + } + fputs(buf, stdout); + G.current_col += buflen; + if (buf[buflen-1] == '\n') + G.current_col = 0; +} + +static void newline(void) +{ + if (G.current_col != 0) + wrapf("\n"); +} + +#ifdef TIOCGWINSZ +static void set_window_size(int rows, int cols) +{ + struct winsize win = { 0, 0, 0, 0 }; + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) { + if (errno != EINVAL) { + goto bail; + } + memset(&win, 0, sizeof(win)); + } + + if (rows >= 0) + win.ws_row = rows; + if (cols >= 0) + win.ws_col = cols; + + if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win)) +bail: + perror_on_device("%s"); +} +#endif + +static void display_window_size(int fancy) +{ + const char *fmt_str = "%s\0%s: no size information for this device"; + unsigned width, height; + + if (get_terminal_width_height(STDIN_FILENO, &width, &height)) { + if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) { + perror_on_device(fmt_str); + } + } else { + wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n", + height, width); + } +} + +static const struct suffix_mult stty_suffixes[] = { + { "b", 512 }, + { "k", 1024 }, + { "B", 1024 }, + { "", 0 } +}; + +static const struct mode_info *find_mode(const char *name) +{ + int i = index_in_strings(mode_name, name); + return i >= 0 ? &mode_info[i] : NULL; +} + +static const struct control_info *find_control(const char *name) +{ + int i = index_in_strings(control_name, name); + return i >= 0 ? &control_info[i] : NULL; +} + +enum { + param_need_arg = 0x80, + param_line = 1 | 0x80, + param_rows = 2 | 0x80, + param_cols = 3 | 0x80, + param_columns = 4 | 0x80, + param_size = 5, + param_speed = 6, + param_ispeed = 7 | 0x80, + param_ospeed = 8 | 0x80, +}; + +static int find_param(const char *name) +{ + static const char params[] ALIGN1 = + "line\0" /* 1 */ + "rows\0" /* 2 */ + "cols\0" /* 3 */ + "columns\0" /* 4 */ + "size\0" /* 5 */ + "speed\0" /* 6 */ + "ispeed\0" + "ospeed\0"; + int i = index_in_strings(params, name) + 1; + if (i == 0) + return 0; + if (i != 5 && i != 6) + i |= 0x80; + return i; +} + +static int recover_mode(const char *arg, struct termios *mode) +{ + int i, n; + unsigned chr; + unsigned long iflag, oflag, cflag, lflag; + + /* Scan into temporaries since it is too much trouble to figure out + the right format for 'tcflag_t' */ + if (sscanf(arg, "%lx:%lx:%lx:%lx%n", + &iflag, &oflag, &cflag, &lflag, &n) != 4) + return 0; + mode->c_iflag = iflag; + mode->c_oflag = oflag; + mode->c_cflag = cflag; + mode->c_lflag = lflag; + arg += n; + for (i = 0; i < NCCS; ++i) { + if (sscanf(arg, ":%x%n", &chr, &n) != 1) + return 0; + mode->c_cc[i] = chr; + arg += n; + } + + /* Fail if there are too many fields */ + if (*arg != '\0') + return 0; + + return 1; +} + +static void display_recoverable(const struct termios *mode, + int UNUSED_PARAM dummy) +{ + int i; + printf("%lx:%lx:%lx:%lx", + (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag, + (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag); + for (i = 0; i < NCCS; ++i) + printf(":%x", (unsigned int) mode->c_cc[i]); + bb_putchar('\n'); +} + +static void display_speed(const struct termios *mode, int fancy) +{ + //____________________ 01234567 8 9 + const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;"; + unsigned long ispeed, ospeed; + + ispeed = cfgetispeed(mode); + ospeed = cfgetospeed(mode); + if (ispeed == 0 || ispeed == ospeed) { + ispeed = ospeed; /* in case ispeed was 0 */ + //________ 0123 4 5 6 7 8 9 + fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;"; + } + if (fancy) fmt_str += 9; + wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed)); +} + +static void do_display(const struct termios *mode, int all) +{ + int i; + tcflag_t *bitsp; + unsigned long mask; + int prev_type = control; + + display_speed(mode, 1); + if (all) + display_window_size(1); +#ifdef __linux__ + wrapf("line = %u;\n", mode->c_line); +#else + newline(); +#endif + + for (i = 0; i != CIDX_min; ++i) { + char ch; + char buf10[10]; + + /* If swtch is the same as susp, don't print both */ +#if VSWTCH == VSUSP + if (i == CIDX_swtch) + continue; +#endif + /* If eof uses the same slot as min, only print whichever applies */ +#if VEOF == VMIN + if (!(mode->c_lflag & ICANON) + && (i == CIDX_eof || i == CIDX_eol) + ) { + continue; + } +#endif + ch = mode->c_cc[control_info[i].offset]; + if (ch == _POSIX_VDISABLE) + strcpy(buf10, ""); + else + visible(ch, buf10, 0); + wrapf("%s = %s;", nth_string(control_name, i), buf10); + } +#if VEOF == VMIN + if ((mode->c_lflag & ICANON) == 0) +#endif + wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]); + newline(); + + for (i = 0; i < NUM_mode_info; ++i) { + if (mode_info[i].flags & OMIT) + continue; + if (mode_info[i].type != prev_type) { + newline(); + prev_type = mode_info[i].type; + } + + bitsp = get_ptr_to_tcflag(mode_info[i].type, mode); + mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits; + if ((*bitsp & mask) == mode_info[i].bits) { + if (all || (mode_info[i].flags & SANE_UNSET)) + wrapf("-%s"+1, nth_string(mode_name, i)); + } else { + if ((all && mode_info[i].flags & REV) + || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) + ) { + wrapf("-%s", nth_string(mode_name, i)); + } + } + } + newline(); +} + +static void sane_mode(struct termios *mode) +{ + int i; + + for (i = 0; i < NUM_control_info; ++i) { +#if VMIN == VEOF + if (i == CIDX_min) + break; +#endif + mode->c_cc[control_info[i].offset] = control_info[i].saneval; + } + + for (i = 0; i < NUM_mode_info; ++i) { + tcflag_t val; + tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode); + + if (!bitsp) + continue; + val = *bitsp & ~((unsigned long)mode_info[i].mask); + if (mode_info[i].flags & SANE_SET) { + *bitsp = val | mode_info[i].bits; + } else + if (mode_info[i].flags & SANE_UNSET) { + *bitsp = val & ~mode_info[i].bits; + } + } +} + +static void set_mode(const struct mode_info *info, int reversed, + struct termios *mode) +{ + tcflag_t *bitsp; + + bitsp = get_ptr_to_tcflag(info->type, mode); + + if (bitsp) { + tcflag_t val = *bitsp & ~info->mask; + if (reversed) + *bitsp = val & ~info->bits; + else + *bitsp = val | info->bits; + return; + } + + /* !bitsp - it's a "combination" mode */ + if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) { + if (reversed) + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + else + mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7; + } else if (info == &mode_info[IDX_oddp]) { + if (reversed) + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + else + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB; + } else if (info == &mode_info[IDX_nl]) { + if (reversed) { + mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR; + mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET; + } else { + mode->c_iflag = mode->c_iflag & ~ICRNL; + if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR; + } + } else if (info == &mode_info[IDX_ek]) { + mode->c_cc[VERASE] = CERASE; + mode->c_cc[VKILL] = CKILL; + } else if (info == &mode_info[IDX_sane]) { + sane_mode(mode); + } else if (info == &mode_info[IDX_cbreak]) { + if (reversed) + mode->c_lflag |= ICANON; + else + mode->c_lflag &= ~ICANON; + } else if (info == &mode_info[IDX_pass8]) { + if (reversed) { + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; + mode->c_iflag |= ISTRIP; + } else { + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + mode->c_iflag &= ~ISTRIP; + } + } else if (info == &mode_info[IDX_litout]) { + if (reversed) { + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; + mode->c_iflag |= ISTRIP; + mode->c_oflag |= OPOST; + } else { + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + mode->c_iflag &= ~ISTRIP; + mode->c_oflag &= ~OPOST; + } + } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) { + if ((info == &mode_info[IDX_raw] && reversed) + || (info == &mode_info[IDX_cooked] && !reversed) + ) { + /* Cooked mode */ + mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; + mode->c_oflag |= OPOST; + mode->c_lflag |= ISIG | ICANON; +#if VMIN == VEOF + mode->c_cc[VEOF] = CEOF; +#endif +#if VTIME == VEOL + mode->c_cc[VEOL] = CEOL; +#endif + } else { + /* Raw mode */ + mode->c_iflag = 0; + mode->c_oflag &= ~OPOST; + mode->c_lflag &= ~(ISIG | ICANON | XCASE); + mode->c_cc[VMIN] = 1; + mode->c_cc[VTIME] = 0; + } + } +#if IXANY + else if (info == &mode_info[IDX_decctlq]) { + if (reversed) + mode->c_iflag |= IXANY; + else + mode->c_iflag &= ~IXANY; + } +#endif +#if TABDLY + else if (info == &mode_info[IDX_tabs]) { + if (reversed) + mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3; + else + mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0; + } +#endif +#if OXTABS + else if (info == &mode_info[IDX_tabs]) { + if (reversed) + mode->c_oflag |= OXTABS; + else + mode->c_oflag &= ~OXTABS; + } +#endif +#if XCASE && IUCLC && OLCUC + else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_LCASE]) { + if (reversed) { + mode->c_lflag &= ~XCASE; + mode->c_iflag &= ~IUCLC; + mode->c_oflag &= ~OLCUC; + } else { + mode->c_lflag |= XCASE; + mode->c_iflag |= IUCLC; + mode->c_oflag |= OLCUC; + } + } +#endif + else if (info == &mode_info[IDX_crt]) { + mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; + } else if (info == &mode_info[IDX_dec]) { + mode->c_cc[VINTR] = 3; /* ^C */ + mode->c_cc[VERASE] = 127; /* DEL */ + mode->c_cc[VKILL] = 21; /* ^U */ + mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; + if (IXANY) mode->c_iflag &= ~IXANY; + } +} + +static void set_control_char_or_die(const struct control_info *info, + const char *arg, struct termios *mode) +{ + unsigned char value; + + if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time]) + value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); + else if (arg[0] == '\0' || arg[1] == '\0') + value = arg[0]; + else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0) + value = _POSIX_VDISABLE; + else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */ + value = arg[1] & 0x1f; /* Non-letters get weird results */ + if (arg[1] == '?') + value = 127; + } else + value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); + mode->c_cc[info->offset] = value; +} + +#define STTY_require_set_attr (1 << 0) +#define STTY_speed_was_set (1 << 1) +#define STTY_verbose_output (1 << 2) +#define STTY_recoverable_output (1 << 3) +#define STTY_noargs (1 << 4) + +int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int stty_main(int argc UNUSED_PARAM, char **argv) +{ + struct termios mode; + void (*output_func)(const struct termios *, int); + const char *file_name = NULL; + int display_all = 0; + int stty_state; + int k; + + INIT_G(); + + stty_state = STTY_noargs; + output_func = do_display; + + /* First pass: only parse/verify command line params */ + k = 0; + while (argv[++k]) { + const struct mode_info *mp; + const struct control_info *cp; + const char *arg = argv[k]; + const char *argnext = argv[k+1]; + int param; + + if (arg[0] == '-') { + int i; + mp = find_mode(arg+1); + if (mp) { + if (!(mp->flags & REV)) + goto invalid_argument; + stty_state &= ~STTY_noargs; + continue; + } + /* It is an option - parse it */ + i = 0; + while (arg[++i]) { + switch (arg[i]) { + case 'a': + stty_state |= STTY_verbose_output; + output_func = do_display; + display_all = 1; + break; + case 'g': + stty_state |= STTY_recoverable_output; + output_func = display_recoverable; + break; + case 'F': + if (file_name) + bb_error_msg_and_die("only one device may be specified"); + file_name = &arg[i+1]; /* "-Fdevice" ? */ + if (!file_name[0]) { /* nope, "-F device" */ + int p = k+1; /* argv[p] is argnext */ + file_name = argnext; + if (!file_name) + bb_error_msg_and_die(bb_msg_requires_arg, "-F"); + /* remove -F param from arg[vc] */ + while (argv[p]) { + argv[p] = argv[p+1]; + ++p; + } + } + goto end_option; + default: + goto invalid_argument; + } + } + end_option: + continue; + } + + mp = find_mode(arg); + if (mp) { + stty_state &= ~STTY_noargs; + continue; + } + + cp = find_control(arg); + if (cp) { + if (!argnext) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + /* called for the side effect of xfunc death only */ + set_control_char_or_die(cp, argnext, &mode); + stty_state &= ~STTY_noargs; + ++k; + continue; + } + + param = find_param(arg); + if (param & param_need_arg) { + if (!argnext) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + ++k; + } + + switch (param) { +#ifdef __linux__ + case param_line: +# ifndef TIOCGWINSZ + xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); + break; +# endif /* else fall-through */ +#endif +#ifdef TIOCGWINSZ + case param_rows: + case param_cols: + case param_columns: + xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); + break; + case param_size: +#endif + case param_speed: + break; + case param_ispeed: + /* called for the side effect of xfunc death only */ + set_speed_or_die(input_speed, argnext, &mode); + break; + case param_ospeed: + /* called for the side effect of xfunc death only */ + set_speed_or_die(output_speed, argnext, &mode); + break; + default: + if (recover_mode(arg, &mode) == 1) break; + if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; + invalid_argument: + bb_error_msg_and_die("invalid argument '%s'", arg); + } + stty_state &= ~STTY_noargs; + } + + /* Specifying both -a and -g is an error */ + if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == + (STTY_verbose_output | STTY_recoverable_output) + ) { + bb_error_msg_and_die("-a and -g are mutually exclusive"); + } + /* Specifying -a or -g with non-options is an error */ + if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) + && !(stty_state & STTY_noargs) + ) { + bb_error_msg_and_die("modes may not be set when -a or -g is used"); + } + + /* Now it is safe to start doing things */ + if (file_name) { + G.device_name = file_name; + xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO); + ndelay_off(STDIN_FILENO); + } + + /* Initialize to all zeroes so there is no risk memcmp will report a + spurious difference in an uninitialized portion of the structure */ + memset(&mode, 0, sizeof(mode)); + if (tcgetattr(STDIN_FILENO, &mode)) + perror_on_device_and_die("%s"); + + if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) { + G.max_col = get_terminal_width(STDOUT_FILENO); + output_func(&mode, display_all); + return EXIT_SUCCESS; + } + + /* Second pass: perform actions */ + k = 0; + while (argv[++k]) { + const struct mode_info *mp; + const struct control_info *cp; + const char *arg = argv[k]; + const char *argnext = argv[k+1]; + int param; + + if (arg[0] == '-') { + mp = find_mode(arg+1); + if (mp) { + set_mode(mp, 1 /* reversed */, &mode); + stty_state |= STTY_require_set_attr; + } + /* It is an option - already parsed. Skip it */ + continue; + } + + mp = find_mode(arg); + if (mp) { + set_mode(mp, 0 /* non-reversed */, &mode); + stty_state |= STTY_require_set_attr; + continue; + } + + cp = find_control(arg); + if (cp) { + ++k; + set_control_char_or_die(cp, argnext, &mode); + stty_state |= STTY_require_set_attr; + continue; + } + + param = find_param(arg); + if (param & param_need_arg) { + ++k; + } + + switch (param) { +#ifdef __linux__ + case param_line: + mode.c_line = xatoul_sfx(argnext, stty_suffixes); + stty_state |= STTY_require_set_attr; + break; +#endif +#ifdef TIOCGWINSZ + case param_cols: + case param_columns: + set_window_size(-1, xatoul_sfx(argnext, stty_suffixes)); + break; + case param_size: + display_window_size(0); + break; + case param_rows: + set_window_size(xatoul_sfx(argnext, stty_suffixes), -1); + break; +#endif + case param_speed: + display_speed(&mode, 0); + break; + case param_ispeed: + set_speed_or_die(input_speed, argnext, &mode); + stty_state |= (STTY_require_set_attr | STTY_speed_was_set); + break; + case param_ospeed: + set_speed_or_die(output_speed, argnext, &mode); + stty_state |= (STTY_require_set_attr | STTY_speed_was_set); + break; + default: + if (recover_mode(arg, &mode) == 1) + stty_state |= STTY_require_set_attr; + else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{ + set_speed_or_die(both_speeds, arg, &mode); + stty_state |= (STTY_require_set_attr | STTY_speed_was_set); + } /* else - impossible (caught in the first pass): + bb_error_msg_and_die("invalid argument '%s'", arg); */ + } + } + + if (stty_state & STTY_require_set_attr) { + struct termios new_mode; + + if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) + perror_on_device_and_die("%s"); + + /* POSIX (according to Zlotnick's book) tcsetattr returns zero if + it performs *any* of the requested operations. This means it + can report 'success' when it has actually failed to perform + some proper subset of the requested operations. To detect + this partial failure, get the current terminal attributes and + compare them to the requested ones */ + + /* Initialize to all zeroes so there is no risk memcmp will report a + spurious difference in an uninitialized portion of the structure */ + memset(&new_mode, 0, sizeof(new_mode)); + if (tcgetattr(STDIN_FILENO, &new_mode)) + perror_on_device_and_die("%s"); + + if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { +/* + * I think the below chunk is not necessary on Linux. + * If you are deleting it, also delete STTY_speed_was_set bit - + * it is only ever checked here. + */ +#if 0 /* was "if CIBAUD" */ + /* SunOS 4.1.3 (at least) has the problem that after this sequence, + tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2); + sometimes (m1 != m2). The only difference is in the four bits + of the c_cflag field corresponding to the baud rate. To save + Sun users a little confusion, don't report an error if this + happens. But suppress the error only if we haven't tried to + set the baud rate explicitly -- otherwise we'd never give an + error for a true failure to set the baud rate */ + + new_mode.c_cflag &= (~CIBAUD); + if ((stty_state & STTY_speed_was_set) + || memcmp(&mode, &new_mode, sizeof(mode)) != 0) +#endif + perror_on_device_and_die("%s: cannot perform all requested operations"); + } + } + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/sum.c b/busybox/coreutils/sum.c new file mode 100644 index 00000000000000..487e93f4acea7a --- /dev/null +++ b/busybox/coreutils/sum.c @@ -0,0 +1,118 @@ +/* vi: set sw=4 ts=4: */ +/* + * sum -- checksum and count the blocks in a file + * Like BSD sum or SysV sum -r, except like SysV sum if -s option is given. + * + * Copyright (C) 86, 89, 91, 1995-2002, 2004 Free Software Foundation, Inc. + * Copyright (C) 2005 by Erik Andersen + * Copyright (C) 2005 by Mike Frysinger + * + * Written by Kayvan Aghaiepour and David MacKenzie + * Taken from coreutils and turned into a busybox applet by Mike Frysinger + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config SUM +//config: bool "sum (4.3 kb)" +//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]..." +//usage:#define sum_full_usage "\n\n" +//usage: "Checksum and count the blocks in a file\n" +//usage: "\n -r Use BSD sum algorithm (1K blocks)" +//usage: "\n -s Use System V sum algorithm (512byte blocks)" + +#include "libbb.h" +#include "common_bufsiz.h" + +enum { SUM_BSD, PRINT_NAME, SUM_SYSV }; + +/* BSD: calculate and print the rotated checksum and the size in 1K blocks + The checksum varies depending on sizeof (int). */ +/* SYSV: calculate and print the checksum and the size in 512-byte blocks */ +/* Return 1 if successful. */ +static unsigned sum_file(const char *file, unsigned type) +{ + 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, COMMON_BUFSIZE); + + if ((ssize_t)bytes_read <= 0) { + r = (fd && close(fd) != 0); + if (!bytes_read && !r) + /* no error */ + break; + bb_simple_perror_msg(file); + return 0; + } + + total_bytes += bytes_read; + if (type >= SUM_SYSV) { + do s += buf[--bytes_read]; while (bytes_read); + } else { + r = 0; + do { + s = (s >> 1) + ((s & 1) << 15); + s += buf[r++]; + s &= 0xffff; /* Keep it within bounds. */ + } while (--bytes_read); + } + } + + if (type < PRINT_NAME) + file = ""; + if (type >= SUM_SYSV) { + r = (s & 0xffff) + ((s & 0xffffffff) >> 16); + s = (r & 0xffff) + (r >> 16); + printf("%u %llu %s\n", s, (total_bytes + 511) / 512, file); + } else + printf("%05u %5llu %s\n", s, (total_bytes + 1023) / 1024, file); + return 1; +#undef buf +} + +int sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sum_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned n; + unsigned type = SUM_BSD; + + n = getopt32(argv, "sr"); + argv += optind; + if (n & 1) type = SUM_SYSV; + /* give the bsd priority over sysv func */ + if (n & 2) type = SUM_BSD; + + if (!argv[0]) { + /* Do not print the name */ + n = sum_file("-", type); + } else { + /* Need to print the name if either + * - more than one file given + * - doing sysv */ + type += (argv[1] || type == SUM_SYSV); + n = 1; + do { + n &= sum_file(*argv, type); + } while (*++argv); + } + return !n; +} diff --git a/busybox/coreutils/sync.c b/busybox/coreutils/sync.c new file mode 100644 index 00000000000000..9be47ab64be4e5 --- /dev/null +++ b/busybox/coreutils/sync.c @@ -0,0 +1,102 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini sync implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * Copyright (C) 2015 by Ari Sundholm + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config SYNC +//config: bool "sync (769 bytes)" +//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: ""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" + +/* This is a NOFORK applet. Be very careful! */ + +int sync_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sync_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM)) +{ +#if !ENABLE_FEATURE_SYNC_FANCY + /* coreutils-6.9 compat */ + bb_warn_ignoring_args(argv[1]); + sync(); + return EXIT_SUCCESS; +#else + unsigned opts; + int ret = EXIT_SUCCESS; + + enum { + OPT_DATASYNC = (1 << 0), + OPT_SYNCFS = (1 << 1), + }; + + opts = getopt32(argv, "^" "df" "\0" "d--f:f--d"); + argv += optind; + + /* Handle the no-argument case. */ + if (!argv[0]) + sync(); + + while (*argv) { + int fd = open_or_warn(*argv, O_RDONLY); + + if (fd < 0) { + ret = EXIT_FAILURE; + goto next; + } + if (opts & OPT_DATASYNC) { + if (fdatasync(fd)) + goto err; + goto do_close; + } + if (opts & OPT_SYNCFS) { + /* + * syncfs is documented to only fail with EBADF, + * which can't happen here. So, no error checks. + */ + syncfs(fd); + goto do_close; + } + if (fsync(fd)) { + err: + bb_simple_perror_msg(*argv); + ret = EXIT_FAILURE; + } + do_close: + close(fd); + next: + ++argv; + } + + return ret; +#endif +} diff --git a/busybox/coreutils/tac.c b/busybox/coreutils/tac.c new file mode 100644 index 00000000000000..6d3ea9c199d5cb --- /dev/null +++ b/busybox/coreutils/tac.c @@ -0,0 +1,117 @@ +/* vi: set sw=4 ts=4: */ +/* + * tac implementation for busybox + * tac - concatenate and print files in reverse + * + * Copyright (C) 2003 Yang Xiaopeng + * Copyright (C) 2007 Natanael Copa + * Copyright (C) 2007 Tito Ragusa + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +/* 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 (4.1 kb)" +//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]..." +//usage:#define tac_full_usage "\n\n" +//usage: "Concatenate FILEs and print them in reverse" + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +struct lstring { + int size; + char buf[1]; +}; + +int tac_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tac_main(int argc UNUSED_PARAM, char **argv) +{ + char **name; + FILE *f; + struct lstring *line = NULL; + llist_t *list = NULL; + int retval = EXIT_SUCCESS; + +#if ENABLE_DESKTOP +/* tac from coreutils 6.9 supports: + -b, --before + attach the separator before instead of after + -r, --regex + interpret the separator as a regular expression + -s, --separator=STRING + use STRING as the separator instead of newline +We support none, but at least we will complain or handle "--": +*/ + getopt32(argv, ""); + argv += optind; +#else + argv++; +#endif + if (!*argv) + *--argv = (char *)"-"; + /* We will read from last file to first */ + name = argv; + while (*name) + name++; + + do { + int ch, i; + + name--; + f = fopen_or_warn_stdin(*name); + if (f == NULL) { + /* error message is printed by fopen_or_warn_stdin */ + retval = EXIT_FAILURE; + continue; + } + + errno = i = 0; + do { + ch = fgetc(f); + if (ch != EOF) { + if (!(i & 0x7f)) + /* Grow on every 128th char */ + line = xrealloc(line, i + 0x7f + sizeof(int) + 1); + line->buf[i++] = ch; + } + if (ch == '\n' || (ch == EOF && i != 0)) { + line = xrealloc(line, i + sizeof(int)); + line->size = i; + llist_add_to(&list, line); + line = NULL; + i = 0; + } + } while (ch != EOF); + /* fgetc sets errno to ENOENT on EOF, we don't want + * to warn on this non-error! */ + if (errno && errno != ENOENT) { + bb_simple_perror_msg(*name); + retval = EXIT_FAILURE; + } + } while (name != argv); + + while (list) { + line = (struct lstring *)list->data; + xwrite(STDOUT_FILENO, line->buf, line->size); + if (ENABLE_FEATURE_CLEAN_UP) { + free(llist_pop(&list)); + } else { + list = list->link; + } + } + + return retval; +} diff --git a/busybox/coreutils/tail.c b/busybox/coreutils/tail.c new file mode 100644 index 00000000000000..7335ba11e2e514 --- /dev/null +++ b/busybox/coreutils/tail.c @@ -0,0 +1,406 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tail implementation for busybox + * + * Copyright (C) 2001 by Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Pretty much rewritten to fix numerous bugs and reduce realloc() calls. + * Bugs fixed (although I may have forgotten one or two... it was pretty bad) + * 1) mixing printf/write without fflush()ing stdout + * 2) no check that any open files are present + * 3) optstring had -q taking an arg + * 4) no error checking on write in some cases, and a warning even then + * 5) q and s interaction bug + * 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 (7.1 kb)" +//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 -q, -s, -v, and -F options" +//config: default y +//config: depends on TAIL +//config: help +//config: These options are provided by GNU tail, but +//config: are not specified in the SUSv3 standard: +//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: "\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 -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: ) +//usage: +//usage:#define tail_example_usage +//usage: "$ tail -n 1 /etc/resolv.conf\n" +//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 { setup_common_bufsiz(); } while (0) + +static void tail_xprint_header(const char *fmt, const char *filename) +{ + if (fdprintf(STDOUT_FILENO, fmt, filename) < 0) + bb_perror_nomsg_and_die(); +} + +static ssize_t tail_read(int fd, char *buf, size_t count) +{ + ssize_t r; + + r = full_read(fd, buf, count); + if (r < 0) { + bb_perror_msg(bb_msg_read_error); + G.exitcode = EXIT_FAILURE; + } + + return r; +} + +#define header_fmt_str "\n==> %s <==\n" + +static unsigned eat_num(const char *p) +{ + if (*p == '-') + p++; + else if (*p == '+') { + p++; + G.from_top = 1; + } + return xatou_sfx(p, bkm_suffixes); +} + +int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tail_main(int argc, char **argv) +{ + unsigned count = 10; + unsigned sleep_period = 1; + const char *str_c, *str_n; + + char *tailbuf; + size_t tailbufsize; + unsigned header_threshhold = 1; + unsigned nfiles; + int i, opt; + + int *fds; + const char *fmt; + int prev_fd; + + INIT_G(); + +#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL + /* Allow legacy syntax of an initial numeric option without -n. */ + if (argv[1] && (argv[1][0] == '+' || argv[1][0] == '-') + && isdigit(argv[1][1]) + ) { + count = eat_num(argv[1]); + argv++; + argc--; + } +#endif + + /* -s NUM, -F imlies -f */ + opt = getopt32(argv, IF_FEATURE_FANCY_TAIL("^") + "fc:n:"IF_FEATURE_FANCY_TAIL("qs:+vF") + IF_FEATURE_FANCY_TAIL("\0" "Ff"), + &str_c, &str_n IF_FEATURE_FANCY_TAIL(,&sleep_period) + ); +#define FOLLOW (opt & 0x1) +#define COUNT_BYTES (opt & 0x2) + //if (opt & 0x1) // -f + if (opt & 0x2) count = eat_num(str_c); // -c + if (opt & 0x4) count = eat_num(str_n); // -n +#if ENABLE_FEATURE_FANCY_TAIL + /* q: make it impossible for nfiles to be > header_threshhold */ + if (opt & 0x8) header_threshhold = UINT_MAX; // -q + //if (opt & 0x10) // -s + if (opt & 0x20) header_threshhold = 0; // -v +# define FOLLOW_RETRY (opt & 0x40) +#else +# define FOLLOW_RETRY 0 +#endif + argc -= optind; + argv += optind; + + /* open all the files */ + fds = xmalloc(sizeof(fds[0]) * (argc + 1)); + if (!argv[0]) { + struct stat statbuf; + + if (fstat(STDIN_FILENO, &statbuf) == 0 + && S_ISFIFO(statbuf.st_mode) + ) { + opt &= ~1; /* clear FOLLOW */ + } + argv[0] = (char *) bb_msg_standard_input; + } + nfiles = i = 0; + do { + int fd = open_or_warn_stdin(argv[i]); + if (fd < 0 && !FOLLOW_RETRY) { + G.exitcode = EXIT_FAILURE; + continue; + } + fds[nfiles] = fd; + argv[nfiles++] = argv[i]; + } while (++i < argc); + + if (!nfiles) + bb_error_msg_and_die("no files"); + + /* prepare the buffer */ + tailbufsize = BUFSIZ; + if (!G.from_top && COUNT_BYTES) { + if (tailbufsize < count + BUFSIZ) { + tailbufsize = count + BUFSIZ; + } + } + /* tail -c1024m REGULAR_FILE doesn't really need 1G mem block. + * (In fact, it doesn't need ANY memory). So delay allocation. + */ + tailbuf = NULL; + + /* tail the files */ + + fmt = header_fmt_str + 1; /* skip leading newline in the header on the first output */ + i = 0; + do { + char *buf; + int taillen; + int newlines_seen; + unsigned seen; + int nread; + int fd = fds[i]; + + if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) + continue; /* may happen with -F */ + + if (nfiles > header_threshhold) { + tail_xprint_header(fmt, argv[i]); + fmt = header_fmt_str; + } + + if (!G.from_top) { + off_t current = lseek(fd, 0, SEEK_END); + if (current > 0) { + unsigned off; + if (COUNT_BYTES) { + /* Optimizing count-bytes case if the file is seekable. + * Beware of backing up too far. + * Also we exclude files with size 0 (because of /proc/xxx) */ + if (count == 0) + continue; /* showing zero bytes is easy :) */ + current -= count; + if (current < 0) + current = 0; + xlseek(fd, current, SEEK_SET); + bb_copyfd_size(fd, STDOUT_FILENO, count); + continue; + } +#if 1 /* This is technically incorrect for *LONG* strings, but very useful */ + /* Optimizing count-lines case if the file is seekable. + * We assume the lines are <64k. + * (Users complain that tail takes too long + * on multi-gigabyte files) */ + off = (count | 0xf); /* for small counts, be more paranoid */ + if (off > (INT_MAX / (64*1024))) + off = (INT_MAX / (64*1024)); + current -= off * (64*1024); + if (current < 0) + current = 0; + xlseek(fd, current, SEEK_SET); +#endif + } + } + + if (!tailbuf) + tailbuf = xmalloc(tailbufsize); + + buf = tailbuf; + taillen = 0; + /* "We saw 1st line/byte". + * Used only by +N code ("start from Nth", 1-based): */ + seen = 1; + newlines_seen = 0; + while ((nread = tail_read(fd, buf, tailbufsize - taillen)) > 0) { + if (G.from_top) { + int nwrite = nread; + if (seen < count) { + /* We need to skip a few more bytes/lines */ + if (COUNT_BYTES) { + nwrite -= (count - seen); + seen += nread; + } else { + char *s = buf; + do { + --nwrite; + if (*s++ == '\n' && ++seen == count) { + break; + } + } while (nwrite); + } + } + if (nwrite > 0) + xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite); + } else if (count) { + if (COUNT_BYTES) { + taillen += nread; + if (taillen > (int)count) { + memmove(tailbuf, tailbuf + taillen - count, count); + taillen = count; + } + } else { + int k = nread; + int newlines_in_buf = 0; + + do { /* count '\n' in last read */ + k--; + if (buf[k] == '\n') { + newlines_in_buf++; + } + } while (k); + + if (newlines_seen + newlines_in_buf < (int)count) { + newlines_seen += newlines_in_buf; + taillen += nread; + } else { + int extra = (buf[nread-1] != '\n'); + char *s; + + k = newlines_seen + newlines_in_buf + extra - count; + s = tailbuf; + while (k) { + if (*s == '\n') { + k--; + } + s++; + } + taillen += nread - (s - tailbuf); + memmove(tailbuf, s, taillen); + newlines_seen = count - extra; + } + if (tailbufsize < (size_t)taillen + BUFSIZ) { + tailbufsize = taillen + BUFSIZ; + tailbuf = xrealloc(tailbuf, tailbufsize); + } + } + buf = tailbuf + taillen; + } + } /* while (tail_read() > 0) */ + if (!G.from_top) { + xwrite(STDOUT_FILENO, tailbuf, taillen); + } + } while (++i < nfiles); + prev_fd = fds[i-1]; + + tailbuf = xrealloc(tailbuf, BUFSIZ); + + fmt = NULL; + + if (FOLLOW) while (1) { + sleep(sleep_period); + + i = 0; + do { + int nread; + const char *filename = argv[i]; + int fd = fds[i]; + + if (FOLLOW_RETRY) { + struct stat sbuf, fsbuf; + + if (fd < 0 + || fstat(fd, &fsbuf) < 0 + || stat(filename, &sbuf) < 0 + || fsbuf.st_dev != sbuf.st_dev + || fsbuf.st_ino != sbuf.st_ino + ) { + int new_fd; + + if (fd >= 0) + close(fd); + new_fd = open(filename, O_RDONLY); + if (new_fd >= 0) { + bb_error_msg("%s has %s; following end of new file", + filename, (fd < 0) ? "appeared" : "been replaced" + ); + } else if (fd >= 0) { + bb_perror_msg("%s has become inaccessible", filename); + } + fds[i] = fd = new_fd; + } + } + if (ENABLE_FEATURE_FANCY_TAIL && fd < 0) + continue; + if (nfiles > header_threshhold) { + fmt = header_fmt_str; + } + for (;;) { + /* tail -f keeps following files even if they are truncated */ + struct stat sbuf; + /* /proc files report zero st_size, don't lseek them */ + if (fstat(fd, &sbuf) == 0 && sbuf.st_size > 0) { + off_t current = lseek(fd, 0, SEEK_CUR); + if (sbuf.st_size < current) + xlseek(fd, 0, SEEK_SET); + } + + nread = tail_read(fd, tailbuf, BUFSIZ); + if (nread <= 0) + break; + if (fmt && (fd != prev_fd)) { + tail_xprint_header(fmt, filename); + fmt = NULL; + prev_fd = fd; + } + xwrite(STDOUT_FILENO, tailbuf, nread); + } + } while (++i < nfiles); + } /* while (1) */ + + if (ENABLE_FEATURE_CLEAN_UP) { + free(fds); + free(tailbuf); + } + return G.exitcode; +} diff --git a/busybox/coreutils/tee.c b/busybox/coreutils/tee.c new file mode 100644 index 00000000000000..f0ec791bb926c9 --- /dev/null +++ b/busybox/coreutils/tee.c @@ -0,0 +1,137 @@ +/* vi: set sw=4 ts=4: */ +/* + * tee implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config TEE +//config: bool "tee (4.3 kb)" +//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 */ + +//usage:#define tee_trivial_usage +//usage: "[-ai] [FILE]..." +//usage:#define tee_full_usage "\n\n" +//usage: "Copy stdin to each FILE, and also to stdout\n" +//usage: "\n -a Append to the given FILEs, don't overwrite" +//usage: "\n -i Ignore interrupt signals (SIGINT)" +//usage: +//usage:#define tee_example_usage +//usage: "$ echo \"Hello\" | tee /tmp/foo\n" +//usage: "$ cat /tmp/foo\n" +//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) +{ + const char *mode = "w\0a"; + FILE **files; + FILE **fp; + char **names; + char **np; + char retval; +//TODO: make unconditional +#if ENABLE_FEATURE_TEE_USE_BLOCK_IO + ssize_t c; +# define buf bb_common_bufsiz1 + setup_common_bufsiz(); +#else + int c; +#endif + retval = getopt32(argv, "ia"); /* 'a' must be 2nd */ + argc -= optind; + argv += optind; + + mode += (retval & 2); /* Since 'a' is the 2nd option... */ + + if (retval & 1) { + signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction. (why?) */ + } + retval = EXIT_SUCCESS; + /* gnu tee ignores SIGPIPE in case one of the output files is a pipe + * that doesn't consume all its input. Good idea... */ + signal(SIGPIPE, SIG_IGN); + + /* Allocate an array of FILE *'s, with one extra for a sentinel. */ + fp = files = xzalloc(sizeof(FILE *) * (argc + 2)); + np = names = argv - 1; + + files[0] = stdout; + goto GOT_NEW_FILE; + do { + *fp = stdout; + if (NOT_LONE_DASH(*argv)) { + *fp = fopen_or_warn(*argv, mode); + if (*fp == NULL) { + retval = EXIT_FAILURE; + argv++; + continue; + } + } + *np = *argv++; + GOT_NEW_FILE: + setbuf(*fp, NULL); /* tee must not buffer output. */ + fp++; + np++; + } while (*argv); + /* names[0] will be filled later */ + +#if ENABLE_FEATURE_TEE_USE_BLOCK_IO + while ((c = safe_read(STDIN_FILENO, buf, COMMON_BUFSIZE)) > 0) { + fp = files; + do + fwrite(buf, 1, c, *fp); + while (*++fp); + } + if (c < 0) { /* Make sure read errors are signaled. */ + retval = EXIT_FAILURE; + } +#else + setvbuf(stdout, NULL, _IONBF, 0); + while ((c = getchar()) != EOF) { + fp = files; + do + putc(c, *fp); + while (*++fp); + } +#endif + + /* Now we need to check for i/o errors on stdin and the various + * output files. Since we know that the first entry in the output + * file table is stdout, we can save one "if ferror" test by + * setting the first entry to stdin and checking stdout error + * status with fflush_stdout_and_exit()... although fflush()ing + * is unnecessary here. */ + np = names; + fp = files; + names[0] = (char *) bb_msg_standard_input; + files[0] = stdin; + do { /* Now check for input and output errors. */ + /* Checking ferror should be sufficient, but we may want to fclose. + * If we do, remember not to close stdin! */ + die_if_ferror(*fp++, *np++); + } while (*fp); + + fflush_stdout_and_exit(retval); +} diff --git a/busybox/coreutils/test.c b/busybox/coreutils/test.c new file mode 100644 index 00000000000000..ddb66ddcec1f17 --- /dev/null +++ b/busybox/coreutils/test.c @@ -0,0 +1,947 @@ +/* vi: set sw=4 ts=4: */ +/* + * test implementation for busybox + * + * Copyright (c) by a whole pile of folks: + * + * test(1); version 7-like -- author Erik Baalbergen + * modified by Eric Gisin to be used as built-in. + * modified by Arnold Robbins to add SVR3 compatibility + * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). + * modified by J.T. Conklin for NetBSD. + * modified by Herbert Xu to be used as built-in in ash. + * modified by Erik Andersen to be used + * in busybox. + * modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty). + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Original copyright notice states: + * "This program is in the Public Domain." + */ +//config:config TEST +//config: bool "test (3.6 kb)" +//config: default y +//config: help +//config: test is used to check file types and compare values, +//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 || TEST1 || TEST2 || ASH_TEST || HUSH_TEST +//config: help +//config: Enable 64-bit support in test. + +//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_TEST) += test.o test_ptr_hack.o +//kbuild:lib-$(CONFIG_HUSH_TEST) += 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" +//usage: "$ echo $?\n" +//usage: "1\n" +//usage: "$ test 1 -eq 1\n" +//usage: "$ echo $?\n" +//usage: "0\n" +//usage: "$ [ -d /etc ]\n" +//usage: "$ echo $?\n" +//usage: "0\n" +//usage: "$ [ -d /junk ]\n" +//usage: "$ echo $?\n" +//usage: "1\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +/* test_main() is called from shells, and we need to be extra careful here. + * This is true regardless of PREFER_APPLETS and SH_STANDALONE + * state. */ + +/* test(1) accepts the following grammar: + oexpr ::= aexpr | aexpr "-o" oexpr ; + aexpr ::= nexpr | nexpr "-a" aexpr ; + nexpr ::= primary | "!" primary + primary ::= unary-operator operand + | operand binary-operator operand + | operand + | "(" oexpr ")" + ; + unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| + "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; + + binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| + "-nt"|"-ot"|"-ef"; + operand ::= +*/ + +/* TODO: handle [[ expr ]] bashism bash-compatibly. + * [[ ]] is meant to be a "better [ ]", with less weird syntax + * and without the risk of variables and quoted strings misinterpreted + * as operators. + * This will require support from shells - we need to know quote status + * of each parameter (see below). + * + * Word splitting and pathname expansion should NOT be performed: + * # a="a b"; [[ $a = "a b" ]] && echo YES + * YES + * # [[ /bin/m* ]] && echo YES + * YES + * + * =~ should do regexp match + * = and == should do pattern match against right side: + * # [[ *a* == bab ]] && echo YES + * # [[ bab == *a* ]] && echo YES + * YES + * != does the negated == (i.e., also with pattern matching). + * Pattern matching is quotation-sensitive: + * # [[ bab == "b"a* ]] && echo YES + * YES + * # [[ bab == b"a*" ]] && echo YES + * + * Conditional operators such as -f must be unquoted literals to be recognized: + * # [[ -e /bin ]] && echo YES + * YES + * # [[ '-e' /bin ]] && echo YES + * bash: conditional binary operator expected... + * # A='-e'; [[ $A /bin ]] && echo YES + * bash: conditional binary operator expected... + * + * || and && should work as -o and -a work in [ ] + * -a and -o aren't recognized (&& and || are to be used instead) + * ( and ) do not need to be quoted unlike in [ ]: + * # [[ ( abc ) && '' ]] && echo YES + * # [[ ( abc ) || '' ]] && echo YES + * YES + * # [[ ( abc ) -o '' ]] && echo YES + * bash: syntax error in conditional expression... + * + * Apart from the above, [[ expr ]] should work as [ expr ] + */ + +#define TEST_DEBUG 0 + +enum token { + EOI, + + FILRD, /* file access */ + FILWR, + FILEX, + + FILEXIST, + + FILREG, /* file type */ + FILDIR, + FILCDEV, + FILBDEV, + FILFIFO, + FILSOCK, + + FILSYM, + FILGZ, + FILTT, + + FILSUID, /* file bit */ + FILSGID, + FILSTCK, + + FILNT, /* file ops */ + FILOT, + FILEQ, + + FILUID, + FILGID, + + STREZ, /* str ops */ + STRNZ, + STREQ, + STRNE, + STRLT, + STRGT, + + INTEQ, /* int ops */ + INTNE, + INTGE, + INTGT, + INTLE, + INTLT, + + UNOT, + BAND, + BOR, + LPAREN, + RPAREN, + OPERAND +}; +#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5) +#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5) +#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2) +#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2) +#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5) +#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2) + +#if TEST_DEBUG +int depth; +#define nest_msg(...) do { \ + depth++; \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__); \ +} while (0) +#define unnest_msg(...) do { \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__); \ + depth--; \ +} while (0) +#define dbg_msg(...) do { \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__); \ +} while (0) +#define unnest_msg_and_return(expr, ...) do { \ + number_t __res = (expr); \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__, res); \ + depth--; \ + return __res; \ +} while (0) +static const char *const TOKSTR[] = { + "EOI", + "FILRD", + "FILWR", + "FILEX", + "FILEXIST", + "FILREG", + "FILDIR", + "FILCDEV", + "FILBDEV", + "FILFIFO", + "FILSOCK", + "FILSYM", + "FILGZ", + "FILTT", + "FILSUID", + "FILSGID", + "FILSTCK", + "FILNT", + "FILOT", + "FILEQ", + "FILUID", + "FILGID", + "STREZ", + "STRNZ", + "STREQ", + "STRNE", + "STRLT", + "STRGT", + "INTEQ", + "INTNE", + "INTGE", + "INTGT", + "INTLE", + "INTLT", + "UNOT", + "BAND", + "BOR", + "LPAREN", + "RPAREN", + "OPERAND" +}; +#else +#define nest_msg(...) ((void)0) +#define unnest_msg(...) ((void)0) +#define dbg_msg(...) ((void)0) +#define unnest_msg_and_return(expr, ...) return expr +#endif + +enum { + UNOP, + BINOP, + BUNOP, + BBINOP, + PAREN +}; + +struct operator_t { + unsigned char op_num, op_type; +}; + +static const struct operator_t ops_table[] = { + { /* "-r" */ FILRD , UNOP }, + { /* "-w" */ FILWR , UNOP }, + { /* "-x" */ FILEX , UNOP }, + { /* "-e" */ FILEXIST, UNOP }, + { /* "-f" */ FILREG , UNOP }, + { /* "-d" */ FILDIR , UNOP }, + { /* "-c" */ FILCDEV , UNOP }, + { /* "-b" */ FILBDEV , UNOP }, + { /* "-p" */ FILFIFO , UNOP }, + { /* "-u" */ FILSUID , UNOP }, + { /* "-g" */ FILSGID , UNOP }, + { /* "-k" */ FILSTCK , UNOP }, + { /* "-s" */ FILGZ , UNOP }, + { /* "-t" */ FILTT , UNOP }, + { /* "-z" */ STREZ , UNOP }, + { /* "-n" */ STRNZ , UNOP }, + { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */ + + { /* "-O" */ FILUID , UNOP }, + { /* "-G" */ FILGID , UNOP }, + { /* "-L" */ FILSYM , UNOP }, + { /* "-S" */ FILSOCK , UNOP }, + { /* "=" */ STREQ , BINOP }, + /* "==" is bashism, http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html + * lists only "=" as comparison operator. + */ + { /* "==" */ STREQ , BINOP }, + { /* "!=" */ STRNE , BINOP }, + { /* "<" */ STRLT , BINOP }, + { /* ">" */ STRGT , BINOP }, + { /* "-eq"*/ INTEQ , BINOP }, + { /* "-ne"*/ INTNE , BINOP }, + { /* "-ge"*/ INTGE , BINOP }, + { /* "-gt"*/ INTGT , BINOP }, + { /* "-le"*/ INTLE , BINOP }, + { /* "-lt"*/ INTLT , BINOP }, + { /* "-nt"*/ FILNT , BINOP }, + { /* "-ot"*/ FILOT , BINOP }, + { /* "-ef"*/ FILEQ , BINOP }, + { /* "!" */ UNOT , BUNOP }, + { /* "-a" */ BAND , BBINOP }, + { /* "-o" */ BOR , BBINOP }, + { /* "(" */ LPAREN , PAREN }, + { /* ")" */ RPAREN , PAREN }, +}; +/* Please keep these two tables in sync */ +static const char ops_texts[] ALIGN1 = + "-r" "\0" + "-w" "\0" + "-x" "\0" + "-e" "\0" + "-f" "\0" + "-d" "\0" + "-c" "\0" + "-b" "\0" + "-p" "\0" + "-u" "\0" + "-g" "\0" + "-k" "\0" + "-s" "\0" + "-t" "\0" + "-z" "\0" + "-n" "\0" + "-h" "\0" + + "-O" "\0" + "-G" "\0" + "-L" "\0" + "-S" "\0" + "=" "\0" + /* "==" is bashism */ + "==" "\0" + "!=" "\0" + "<" "\0" + ">" "\0" + "-eq" "\0" + "-ne" "\0" + "-ge" "\0" + "-gt" "\0" + "-le" "\0" + "-lt" "\0" + "-nt" "\0" + "-ot" "\0" + "-ef" "\0" + "!" "\0" + "-a" "\0" + "-o" "\0" + "(" "\0" + ")" "\0" +; + + +#if ENABLE_FEATURE_TEST_64 +typedef int64_t number_t; +#else +typedef int number_t; +#endif + + +/* We try to minimize both static and stack usage. */ +struct test_statics { + char **args; + /* set only by check_operator(), either to bogus struct + * or points to matching operator_t struct. Never NULL. */ + const struct operator_t *last_operator; + gid_t *group_array; + int ngroups; + jmp_buf leaving; +}; + +/* See test_ptr_hack.c */ +extern struct test_statics *const test_ptr_to_statics; + +#define S (*test_ptr_to_statics) +#define args (S.args ) +#define last_operator (S.last_operator) +#define group_array (S.group_array ) +#define ngroups (S.ngroups ) +#define leaving (S.leaving ) + +#define INIT_S() do { \ + (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \ + barrier(); \ +} while (0) +#define DEINIT_S() do { \ + free(group_array); \ + free(test_ptr_to_statics); \ +} while (0) + +static number_t primary(enum token n); + +static void syntax(const char *op, const char *msg) NORETURN; +static void syntax(const char *op, const char *msg) +{ + if (op && *op) { + bb_error_msg("%s: %s", op, msg); + } else { + bb_error_msg("%s: %s"+4, msg); + } + longjmp(leaving, 2); +} + +/* atoi with error detection */ +//XXX: FIXME: duplicate of existing libbb function? +static number_t getn(const char *s) +{ + char *p; +#if ENABLE_FEATURE_TEST_64 + long long r; +#else + long r; +#endif + + errno = 0; +#if ENABLE_FEATURE_TEST_64 + r = strtoll(s, &p, 10); +#else + r = strtol(s, &p, 10); +#endif + + if (errno != 0) + syntax(s, "out of range"); + + if (p == s || *(skip_whitespace(p)) != '\0') + syntax(s, "bad number"); + + return r; +} + +/* UNUSED +static int newerf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime); +} + +static int olderf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime); +} + +static int equalf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && + b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); +} +*/ + + +static enum token check_operator(const char *s) +{ + static const struct operator_t no_op = { + .op_num = -1, + .op_type = -1 + }; + int n; + + last_operator = &no_op; + if (s == NULL) + return EOI; + n = index_in_strings(ops_texts, s); + if (n < 0) + return OPERAND; + last_operator = &ops_table[n]; + return ops_table[n].op_num; +} + + +static int binop(void) +{ + const char *opnd1, *opnd2; + const struct operator_t *op; + number_t val1, val2; + + opnd1 = *args; + check_operator(*++args); + op = last_operator; + + opnd2 = *++args; + if (opnd2 == NULL) + syntax(args[-1], "argument expected"); + + if (is_int_op(op->op_num)) { + val1 = getn(opnd1); + val2 = getn(opnd2); + if (op->op_num == INTEQ) + return val1 == val2; + if (op->op_num == INTNE) + return val1 != val2; + if (op->op_num == INTGE) + return val1 >= val2; + if (op->op_num == INTGT) + return val1 > val2; + if (op->op_num == INTLE) + return val1 <= val2; + /*if (op->op_num == INTLT)*/ + return val1 < val2; + } + if (is_str_op(op->op_num)) { + val1 = strcmp(opnd1, opnd2); + if (op->op_num == STREQ) + return val1 == 0; + if (op->op_num == STRNE) + return val1 != 0; + if (op->op_num == STRLT) + return val1 < 0; + /*if (op->op_num == STRGT)*/ + return val1 > 0; + } + /* We are sure that these three are by now the only binops we didn't check + * yet, so we do not check if the class is correct: + */ +/* if (is_file_op(op->op_num)) */ + { + struct stat b1, b2; + + if (stat(opnd1, &b1) || stat(opnd2, &b2)) + return 0; /* false, since at least one stat failed */ + if (op->op_num == FILNT) + return b1.st_mtime > b2.st_mtime; + if (op->op_num == FILOT) + return b1.st_mtime < b2.st_mtime; + /*if (op->op_num == FILEQ)*/ + return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; + } + /*return 1; - NOTREACHED */ +} + +static void initialize_group_array(void) +{ + group_array = bb_getgroups(&ngroups, NULL); +} + +/* Return non-zero if GID is one that we have in our groups list. */ +//XXX: FIXME: duplicate of existing libbb function? +// see toplevel TODO file: +// possible code duplication ingroup() and is_a_group_member() +static int is_a_group_member(gid_t gid) +{ + int i; + + /* Short-circuit if possible, maybe saving a call to getgroups(). */ + if (gid == getgid() || gid == getegid()) + return 1; + + if (ngroups == 0) + initialize_group_array(); + + /* Search through the list looking for GID. */ + for (i = 0; i < ngroups; i++) + if (gid == group_array[i]) + return 1; + + return 0; +} + + +/* Do the same thing access(2) does, but use the effective uid and gid, + and don't make the mistake of telling root that any file is + executable. */ +static int test_eaccess(struct stat *st, int mode) +{ + unsigned int euid = geteuid(); + + if (euid == 0) { + /* Root can read or write any file. */ + if (mode != X_OK) + return 0; + + /* Root can execute any file that has any one of the execute + * bits set. */ + if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + return 0; + } + + if (st->st_uid == euid) /* owner */ + mode <<= 6; + else if (is_a_group_member(st->st_gid)) + mode <<= 3; + + if (st->st_mode & mode) + return 0; + + return -1; +} + + +static int filstat(char *nm, enum token mode) +{ + struct stat s; + unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */ + + if (mode == FILSYM) { +#ifdef S_IFLNK + if (lstat(nm, &s) == 0) { + i = S_IFLNK; + goto filetype; + } +#endif + return 0; + } + + if (stat(nm, &s) != 0) + return 0; + if (mode == FILEXIST) + return 1; + if (is_file_access(mode)) { + if (mode == FILRD) + i = R_OK; + if (mode == FILWR) + i = W_OK; + if (mode == FILEX) + i = X_OK; + return test_eaccess(&s, i) == 0; + } + if (is_file_type(mode)) { + if (mode == FILREG) + i = S_IFREG; + if (mode == FILDIR) + i = S_IFDIR; + if (mode == FILCDEV) + i = S_IFCHR; + if (mode == FILBDEV) + i = S_IFBLK; + if (mode == FILFIFO) { +#ifdef S_IFIFO + i = S_IFIFO; +#else + return 0; +#endif + } + if (mode == FILSOCK) { +#ifdef S_IFSOCK + i = S_IFSOCK; +#else + return 0; +#endif + } + filetype: + return ((s.st_mode & S_IFMT) == i); + } + if (is_file_bit(mode)) { + if (mode == FILSUID) + i = S_ISUID; + if (mode == FILSGID) + i = S_ISGID; + if (mode == FILSTCK) + i = S_ISVTX; + return ((s.st_mode & i) != 0); + } + if (mode == FILGZ) + return s.st_size > 0L; + if (mode == FILUID) + return s.st_uid == geteuid(); + if (mode == FILGID) + return s.st_gid == getegid(); + return 1; /* NOTREACHED */ +} + + +static number_t nexpr(enum token n) +{ + number_t res; + + nest_msg(">nexpr(%s)\n", TOKSTR[n]); + if (n == UNOT) { + n = check_operator(*++args); + if (n == EOI) { + /* special case: [ ! ], [ a -a ! ] are valid */ + /* IOW, "! ARG" may miss ARG */ + args--; + unnest_msg("aexpr(%s)\n", TOKSTR[n]); + res = nexpr(n); + dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]); + if (check_operator(*++args) == BAND) { + dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]); + res = aexpr(check_operator(*++args)) && res; + unnest_msg("oexpr(%s)\n", TOKSTR[n]); + res = aexpr(n); + dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]); + if (check_operator(*++args) == BOR) { + dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]); + res = oexpr(check_operator(*++args)) || res; + unnest_msg("primary(%s)\n", TOKSTR[n]); + if (n == EOI) { + syntax(NULL, "argument expected"); + } + if (n == LPAREN) { + res = oexpr(check_operator(*++args)); + if (check_operator(*++args) != RPAREN) + syntax(NULL, "closing paren expected"); + unnest_msg("op_type == BINOP) + unnest_msg_and_return(binop(), "op_type == UNOP) { + /* unary expression */ + if (args[1] == NULL) +// syntax(args0_op->op_text, "argument expected"); + goto check_emptiness; + args++; + if (n == STREZ) + unnest_msg_and_return(args[0][0] == '\0', "op_type == BINOP) { + /* args[2] is known to be NULL, isn't it bound to fail? */ + unnest_msg_and_return(binop(), "op_type == BINOP) { + /* "test [!] arg1 arg2" */ + args = argv; + res = (binop() == 0); + ret_special: + /* If there was leading "!" op... */ + res ^= negate; + goto ret; + } + /* """If $1 is '(' and $3 is ')', perform the unary test of $2.""" + * Looks like this works without additional coding. + */ + goto check_negate; + } + /* argv[3] exists (at least 4 args), is it exactly 4 args? */ + if (!argv[4]) { + /* + * """ 4 arguments: + * If $1 is '!', negate the three-argument test of $2, $3, and $4. + * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3. + * """ + * Example why code below is necessary: test '(' ! -e ')' + */ + if (LONE_CHAR(argv[0], '(') + && LONE_CHAR(argv[3], ')') + ) { + /* "test [!] ( x y )" */ + argv[3] = NULL; + argv++; + } + } + } + check_negate: + if (LONE_CHAR(argv[0], '!')) { + argv++; + negate ^= 1; + goto again; + } + } + + res = !oexpr(check_operator(*args)); + + if (*args != NULL && *++args != NULL) { + /* Examples: + * test 3 -lt 5 6 + * test -t 1 2 + */ + bb_error_msg("%s: unknown operand", *args); + res = 2; + } + ret: + DEINIT_S(); + return res; +} diff --git a/busybox/coreutils/test_ptr_hack.c b/busybox/coreutils/test_ptr_hack.c new file mode 100644 index 00000000000000..5ba9dcc68e72ee --- /dev/null +++ b/busybox/coreutils/test_ptr_hack.c @@ -0,0 +1,23 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2008 by Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +struct test_statics; + +#ifndef GCC_COMBINE + +/* We cheat here. It is declared as const ptr in libbb.h, + * but here we make it live in R/W memory */ +struct test_statics *test_ptr_to_statics; + +#else + +/* gcc -combine will see through and complain */ +/* Using alternative method which is more likely to break + * on weird architectures, compilers, linkers and so on */ +struct test_statics *const test_ptr_to_statics __attribute__ ((section (".data"))); + +#endif diff --git a/busybox/coreutils/timeout.c b/busybox/coreutils/timeout.c new file mode 100644 index 00000000000000..4a6117f5942414 --- /dev/null +++ b/busybox/coreutils/timeout.c @@ -0,0 +1,127 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * COPYING NOTES + * + * timeout.c -- a timeout handler for shell commands + * + * Copyright (C) 2005-6, Roberto A. Foglietta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * REVISION NOTES: + * released 17-11-2005 by Roberto A. Foglietta + * talarm 04-12-2005 by Roberto A. Foglietta + * modified 05-12-2005 by Roberto A. Foglietta + * sizerdct 06-12-2005 by Roberto A. Foglietta + * splitszf 12-05-2006 by Roberto A. Foglietta + * rewrite 14-11-2008 vda + */ +//config:config TIMEOUT +//config: bool "timeout (5.5 kb)" +//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" +//usage:#define timeout_full_usage "\n\n" +//usage: "Runs PROG. Sends SIG to it if it is not gone in SECS seconds.\n" +//usage: "Defaults: SECS: 10, SIG: TERM." + +#include "libbb.h" + +int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int timeout_main(int argc UNUSED_PARAM, char **argv) +{ + int signo; + int status; + int parent = 0; + int timeout = 10; + pid_t pid; +#if !BB_MMU + char *sv1, *sv2; +#endif + const char *opt_s = "TERM"; + + /* -p option is not documented, it is needed to support NOMMU. */ + + /* -t SECONDS; -p PARENT_PID */ + /* '+': stop at first non-option */ + 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) + bb_error_msg_and_die("unknown signal '%s'", opt_s); + + /* We want to create a grandchild which will watch + * and kill the grandparent. Other methods: + * making parent watch child disrupts parent<->child link + * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" - + * it's better if service_prog is a child of tcpsvd!), + * making child watch parent results in programs having + * unexpected children. */ + + if (parent) /* we were re-execed, already grandchild */ + goto grandchild; + if (!argv[optind]) /* no PROG? */ + bb_show_usage(); + +#if !BB_MMU + sv1 = argv[optind]; + sv2 = argv[optind + 1]; +#endif + pid = xvfork(); + if (pid == 0) { + /* Child: spawn grandchild and exit */ + parent = getppid(); +#if !BB_MMU + argv[optind] = xasprintf("-p%u", parent); + argv[optind + 1] = NULL; +#endif + /* NB: exits with nonzero on error: */ + bb_daemonize_or_rexec(0, argv); + /* Here we are grandchild. Sleep, then kill grandparent */ + grandchild: + /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */ + while (1) { + sleep(1); + if (--timeout <= 0) + break; + if (kill(parent, 0)) { + /* process is gone */ + return EXIT_SUCCESS; + } + } + kill(parent, signo); + return EXIT_SUCCESS; + } + + /* Parent */ + wait(&status); /* wait for child to die */ + /* Did intermediate [v]fork or exec fail? */ + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + return EXIT_FAILURE; + /* Ok, exec a program as requested */ + argv += optind; +#if !BB_MMU + argv[0] = sv1; + argv[1] = sv2; +#endif + BB_EXECVP_or_die(argv); +} diff --git a/busybox/coreutils/touch.c b/busybox/coreutils/touch.c new file mode 100644 index 00000000000000..8577615781284b --- /dev/null +++ b/busybox/coreutils/touch.c @@ -0,0 +1,196 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini touch implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* 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. + */ +//config:config TOUCH +//config: bool "touch (5.8 kb)" +//config: default y +//config: help +//config: touch is used to create or change the access and/or +//config: modification timestamp of specified files. +//config: +//config:config FEATURE_TOUCH_NODEREF +//config: bool "Add support for -h" +//config: default y +//config: depends on TOUCH +//config: help +//config: Enable touch to have the -h option. +//config: This requires libc support for lutimes() function. +//config: +//config:config FEATURE_TOUCH_SUSV3 +//config: bool "Add support for SUSV3 features (-d -t -r)" +//config: default y +//config: depends on TOUCH +//config: help +//config: Enable touch to use a reference file or a given date/time argument. + +//applet:IF_TOUCH(APPLET_NOFORK(touch, touch, BB_DIR_BIN, BB_SUID_DROP, touch)) + +//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" +//usage: "Update the last-modified date on the given FILE[s]\n" +//usage: "\n -c Don't create files" +//usage: IF_FEATURE_TOUCH_NODEREF( +//usage: "\n -h Don't follow links" +//usage: ) +//usage: IF_FEATURE_TOUCH_SUSV3( +//usage: "\n -d DT Date/time to use" +//usage: "\n -t DT Date/time to use" +//usage: "\n -r FILE Use FILE's date/time" +//usage: ) +//usage: +//usage:#define touch_example_usage +//usage: "$ ls -l /tmp/foo\n" +//usage: "/bin/ls: /tmp/foo: No such file or directory\n" +//usage: "$ touch /tmp/foo\n" +//usage: "$ ls -l /tmp/foo\n" +//usage: "-rw-rw-r-- 1 andersen andersen 0 Apr 15 01:11 /tmp/foo\n" + +/* coreutils implements: + * -a change only the access time + * -c, --no-create + * do not create any files + * -d, --date=STRING + * parse STRING and use it instead of current time + * -f (ignored, BSD compat) + * -m change only the modification time + * -h, --no-dereference + * -r, --reference=FILE + * use this file's times instead of current time + * -t STAMP + * use [[CC]YY]MMDDhhmm[.ss] instead of current time + * --time=WORD + * 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) +{ + int fd; + int status = EXIT_SUCCESS; + int opts; + enum { + OPT_c = (1 << 0), + OPT_r = (1 << 1) * ENABLE_FEATURE_TOUCH_SUSV3, + OPT_d = (1 << 2) * ENABLE_FEATURE_TOUCH_SUSV3, + OPT_t = (1 << 3) * ENABLE_FEATURE_TOUCH_SUSV3, + OPT_h = (1 << 4) * ENABLE_FEATURE_TOUCH_NODEREF, + }; +#if ENABLE_FEATURE_TOUCH_SUSV3 +# if ENABLE_LONG_OPTS + static const char touch_longopts[] ALIGN1 = + /* name, has_arg, val */ + "no-create\0" No_argument "c" + "reference\0" Required_argument "r" + "date\0" Required_argument "d" + IF_FEATURE_TOUCH_NODEREF("no-dereference\0" No_argument "h") + ; +# define GETOPT32 getopt32long +# define LONGOPTS ,touch_longopts +# else +# define GETOPT32 getopt32 +# define LONGOPTS +# endif + char *reference_file = NULL; + char *date_str = NULL; + struct timeval timebuf[2]; + timebuf[1].tv_usec = timebuf[0].tv_usec = 0; +#else +# define reference_file NULL +# define date_str NULL +# define timebuf ((struct timeval*)NULL) +# define GETOPT32 getopt32 +# define LONGOPTS +#endif + + /* -d and -t both set time. In coreutils, + * accepted data format differs a bit between -d and -t. + * We accept the same formats for both */ + opts = GETOPT32(argv, "c" IF_FEATURE_TOUCH_SUSV3("r:d:t:") + IF_FEATURE_TOUCH_NODEREF("h") + /*ignored:*/ "fma" + LONGOPTS + IF_FEATURE_TOUCH_SUSV3(, &reference_file) + IF_FEATURE_TOUCH_SUSV3(, &date_str) + IF_FEATURE_TOUCH_SUSV3(, &date_str) + ); + + argv += optind; + if (!*argv) { + bb_show_usage(); + } + + if (reference_file) { + struct stat stbuf; + xstat(reference_file, &stbuf); + timebuf[1].tv_sec = timebuf[0].tv_sec = stbuf.st_mtime; + /* Can use .st_mtim.tv_nsec + * (or is it .st_mtimensec?? see date.c) + * to set microseconds too. + */ + } + + if (date_str) { + struct tm tm_time; + time_t t; + + //memset(&tm_time, 0, sizeof(tm_time)); + /* Better than memset: makes "HH:MM" dates meaningful */ + time(&t); + localtime_r(&t, &tm_time); + parse_datestr(date_str, &tm_time); + + /* Correct any day of week and day of year etc. fields */ + tm_time.tm_isdst = -1; /* Be sure to recheck dst */ + t = validate_tm_time(date_str, &tm_time); + + timebuf[1].tv_sec = timebuf[0].tv_sec = t; + } + + do { + int result; + result = ( +#if ENABLE_FEATURE_TOUCH_NODEREF + (opts & OPT_h) ? lutimes : +#endif + utimes)(*argv, (reference_file || date_str) ? timebuf : NULL); + if (result != 0) { + if (errno == ENOENT) { /* no such file? */ + if (opts & OPT_c) { + /* Creation is disabled, so ignore */ + continue; + } + /* Try to create the file */ + fd = open(*argv, O_RDWR | O_CREAT, 0666); + if (fd >= 0) { + xclose(fd); + if (reference_file || date_str) + utimes(*argv, timebuf); + continue; + } + } + status = EXIT_FAILURE; + bb_simple_perror_msg(*argv); + } + } while (*++argv); + + return status; +} diff --git a/busybox/coreutils/tr.c b/busybox/coreutils/tr.c new file mode 100644 index 00000000000000..10284e1c9f9331 --- /dev/null +++ b/busybox/coreutils/tr.c @@ -0,0 +1,360 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tr implementation for busybox + * + * Copyright (c) 1987,1997, Prentice Hall All rights reserved. + * + * The name of Prentice Hall may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * Copyright (c) Michiel Huisjes + * + * This version of tr is adapted from Minix tr and was modified + * by Erik Andersen to be used in busybox. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* http://www.opengroup.org/onlinepubs/009695399/utilities/tr.html + * TODO: graph, print + */ +//config:config TR +//config: bool "tr (5.5 kb)" +//config: default y +//config: help +//config: tr is used to squeeze, and/or delete characters from standard +//config: input, writing to standard output. +//config: +//config:config FEATURE_TR_CLASSES +//config: bool "Enable character classes (such as [:upper:])" +//config: default y +//config: depends on TR +//config: help +//config: Enable character classes, enabling commands such as: +//config: tr [:upper:] [:lower:] to convert input into lowercase. +//config: +//config:config FEATURE_TR_EQUIV +//config: bool "Enable equivalence classes" +//config: default y +//config: depends on TR +//config: help +//config: Enable equivalence classes, which essentially add the enclosed +//config: character to the current set. For instance, tr [=a=] xyz would +//config: replace all instances of 'a' with 'xyz'. This option is mainly +//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" +//usage: "Translate, squeeze, or delete characters from stdin, writing to stdout\n" +//usage: "\n -c Take complement of STRING1" +//usage: "\n -d Delete input characters coded STRING1" +//usage: "\n -s Squeeze multiple output characters of STRING2 into one character" +//usage: +//usage:#define tr_example_usage +//usage: "$ echo \"gdkkn vnqkc\" | tr [a-y] [b-z]\n" +//usage: "hello world\n" + +#include "libbb.h" + +enum { + ASCII = 256, + /* string buffer needs to be at least as big as the whole "alphabet". + * BUFSIZ == ASCII is ok, but we will realloc in expand + * even for smallest patterns, let's avoid that by using *2: + */ + TR_BUFSIZ = (BUFSIZ > ASCII*2) ? BUFSIZ : ASCII*2, +}; + +static void map(char *pvector, + char *string1, unsigned string1_len, + char *string2, unsigned string2_len) +{ + char last = '0'; + unsigned i, j; + + for (j = 0, i = 0; i < string1_len; i++) { + if (string2_len <= j) + pvector[(unsigned char)(string1[i])] = last; + else + pvector[(unsigned char)(string1[i])] = last = string2[j++]; + } +} + +/* supported constructs: + * Ranges, e.g., 0-9 ==> 0123456789 + * Escapes, e.g., \a ==> Control-G + * Character classes, e.g. [:upper:] ==> A...Z + * Equiv classess, e.g. [=A=] ==> A (hmmmmmmm?) + * not supported: + * [x*N] - repeat char x N times + * [x*] - repeat char x until it fills STRING2: + * # echo qwe123 | /usr/bin/tr 123456789 '[d]' + * qwe[d] + * # echo qwe123 | /usr/bin/tr 123456789 '[d*]' + * qweddd + */ +static unsigned expand(char *arg, char **buffer_p) +{ + char *buffer = *buffer_p; + unsigned pos = 0; + unsigned size = TR_BUFSIZ; + unsigned i; /* can't be unsigned char: must be able to hold 256 */ + unsigned char ac; + + while (*arg) { + if (pos + ASCII > size) { + size += ASCII; + *buffer_p = buffer = xrealloc(buffer, size); + } + if (*arg == '\\') { + const char *z; + arg++; + 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]; + if (ac == '\0') { /* "0-": copy verbatim */ + buffer[pos++] = *arg++; /* copy '0' */ + 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++; + continue; + } + if ((ENABLE_FEATURE_TR_CLASSES || ENABLE_FEATURE_TR_EQUIV) + && *arg == '[' + ) { + arg++; + i = (unsigned char) *arg++; + /* "[xyz...". i=x, arg points to y */ + if (ENABLE_FEATURE_TR_CLASSES && i == ':') { /* [:class:] */ +#define CLO ":]\0" + static const char classes[] ALIGN1 = + "alpha"CLO "alnum"CLO "digit"CLO + "lower"CLO "upper"CLO "space"CLO + "blank"CLO "punct"CLO "cntrl"CLO + "xdigit"CLO; + enum { + CLASS_invalid = 0, /* we increment the retval */ + CLASS_alpha = 1, + CLASS_alnum = 2, + CLASS_digit = 3, + CLASS_lower = 4, + CLASS_upper = 5, + CLASS_space = 6, + CLASS_blank = 7, + CLASS_punct = 8, + CLASS_cntrl = 9, + CLASS_xdigit = 10, + //CLASS_graph = 11, + //CLASS_print = 12, + }; + smalluint j; + char *tmp; + + /* xdigit needs 8, not 7 */ + i = 7 + (arg[0] == 'x'); + tmp = xstrndup(arg, i); + j = index_in_strings(classes, tmp) + 1; + free(tmp); + + if (j == CLASS_invalid) + goto skip_bracket; + + arg += i; + if (j == CLASS_alnum || j == CLASS_digit || j == CLASS_xdigit) { + for (i = '0'; i <= '9'; i++) + buffer[pos++] = i; + } + if (j == CLASS_alpha || j == CLASS_alnum || j == CLASS_upper) { + for (i = 'A'; i <= 'Z'; i++) + buffer[pos++] = i; + } + if (j == CLASS_alpha || j == CLASS_alnum || j == CLASS_lower) { + for (i = 'a'; i <= 'z'; i++) + buffer[pos++] = i; + } + if (j == CLASS_space || j == CLASS_blank) { + buffer[pos++] = '\t'; + if (j == CLASS_space) { + buffer[pos++] = '\n'; + buffer[pos++] = '\v'; + buffer[pos++] = '\f'; + buffer[pos++] = '\r'; + } + buffer[pos++] = ' '; + } + if (j == CLASS_punct || j == CLASS_cntrl) { + for (i = '\0'; i < ASCII; i++) { + if ((j == CLASS_punct && isprint_asciionly(i) && !isalnum(i) && !isspace(i)) + || (j == CLASS_cntrl && iscntrl(i)) + ) { + buffer[pos++] = i; + } + } + } + if (j == CLASS_xdigit) { + for (i = 'A'; i <= 'F'; i++) { + buffer[pos + 6] = i | 0x20; + buffer[pos++] = i; + } + pos += 6; + } + continue; + } + /* "[xyz...", i=x, arg points to y */ + if (ENABLE_FEATURE_TR_EQUIV && i == '=') { /* [=CHAR=] */ + buffer[pos++] = *arg; /* copy CHAR */ + if (!arg[0] || arg[1] != '=' || arg[2] != ']') + bb_show_usage(); + arg += 3; /* skip CHAR=] */ + continue; + } + /* The rest of "[xyz..." cases is treated as normal + * string, "[" has no special meaning here: + * tr "[a-z]" "[A-Z]" can be written as tr "a-z" "A-Z", + * also try tr "[a-z]" "_A-Z+" and you'll see that + * [] is not special here. + */ + skip_bracket: + arg -= 2; /* points to "[" in "[xyz..." */ + } + buffer[pos++] = *arg++; + } + return pos; +} + +/* NB: buffer is guaranteed to be at least TR_BUFSIZE + * (which is >= ASCII) big. + */ +static int complement(char *buffer, int buffer_len) +{ + int len; + char conv[ASCII]; + unsigned char ch; + + len = 0; + ch = '\0'; + while (1) { + if (memchr(buffer, ch, buffer_len) == NULL) + conv[len++] = ch; + if (++ch == '\0') + break; + } + memcpy(buffer, conv, len); + return len; +} + +int tr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tr_main(int argc UNUSED_PARAM, char **argv) +{ + int i; + smalluint opts; + ssize_t read_chars; + size_t in_index, out_index; + unsigned last = UCHAR_MAX + 1; /* not equal to any char */ + unsigned char coded, c; + char *str1 = xmalloc(TR_BUFSIZ); + char *str2 = xmalloc(TR_BUFSIZ); + int str2_length; + int str1_length; + char *vector = xzalloc(ASCII * 3); + char *invec = vector + ASCII; + char *outvec = vector + ASCII * 2; + +#define TR_OPT_complement (3 << 0) +#define TR_OPT_delete (1 << 2) +#define TR_OPT_squeeze_reps (1 << 3) + + for (i = 0; i < ASCII; i++) { + vector[i] = i; + /*invec[i] = outvec[i] = FALSE; - done by xzalloc */ + } + + /* -C/-c difference is that -C complements "characters", + * and -c complements "values" (binary bytes I guess). + * In POSIX locale, these are the same. + */ + + /* '+': stop at first non-option */ + opts = getopt32(argv, "^+" "Ccds" "\0" "-1"); + argv += optind; + + str1_length = expand(*argv++, &str1); + str2_length = 0; + if (opts & TR_OPT_complement) + str1_length = complement(str1, str1_length); + if (*argv) { + if (argv[0][0] == '\0') + bb_error_msg_and_die("STRING2 cannot be empty"); + str2_length = expand(*argv, &str2); + map(vector, str1, str1_length, + str2, str2_length); + } + for (i = 0; i < str1_length; i++) + invec[(unsigned char)(str1[i])] = TRUE; + for (i = 0; i < str2_length; i++) + outvec[(unsigned char)(str2[i])] = TRUE; + + goto start_from; + + /* In this loop, str1 space is reused as input buffer, + * str2 - as output one. */ + for (;;) { + /* If we're out of input, flush output and read more input. */ + if ((ssize_t)in_index == read_chars) { + if (out_index) { + xwrite(STDOUT_FILENO, str2, out_index); + start_from: + out_index = 0; + } + read_chars = safe_read(STDIN_FILENO, str1, TR_BUFSIZ); + if (read_chars <= 0) { + if (read_chars < 0) + bb_perror_msg_and_die(bb_msg_read_error); + break; + } + in_index = 0; + } + c = str1[in_index++]; + if ((opts & TR_OPT_delete) && invec[c]) + continue; + coded = vector[c]; + if ((opts & TR_OPT_squeeze_reps) && last == coded + && (invec[c] || outvec[coded]) + ) { + continue; + } + str2[out_index++] = last = coded; + } + + if (ENABLE_FEATURE_CLEAN_UP) { + free(vector); + free(str2); + free(str1); + } + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/true.c b/busybox/coreutils/true.c new file mode 100644 index 00000000000000..400388c34c99f4 --- /dev/null +++ b/busybox/coreutils/true.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini true implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config TRUE +//config: bool "true (tiny)" +//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 */ + +/* "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" +//usage: "0\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int true_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int true_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/truncate.c b/busybox/coreutils/truncate.c new file mode 100644 index 00000000000000..f693570aa1ba55 --- /dev/null +++ b/busybox/coreutils/truncate.c @@ -0,0 +1,86 @@ +/* + * Mini truncate implementation for busybox + * + * Copyright (C) 2015 by Ari Sundholm + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config TRUNCATE +//config: bool "truncate (4.7 kb)" +//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), + }; + + opts = getopt32(argv, "^" "cs:" "\0" "s:-1", &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/busybox/coreutils/tty.c b/busybox/coreutils/tty.c new file mode 100644 index 00000000000000..18ad7c56624059 --- /dev/null +++ b/busybox/coreutils/tty.c @@ -0,0 +1,66 @@ +/* vi: set sw=4 ts=4: */ +/* + * tty implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config TTY +//config: bool "tty (3.3 kb)" +//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_NOFORK(tty, tty, BB_DIR_USR_BIN, BB_SUID_DROP, tty)) + +//kbuild:lib-$(CONFIG_TTY) += tty.o + +/* BB_AUDIT SUSv4 compliant */ +/* http://www.opengroup.org/onlinepubs/9699919799/utilities/tty.html */ + +//usage:#define tty_trivial_usage +//usage: "" +//usage:#define tty_full_usage "\n\n" +//usage: "Print file name of stdin's terminal" +//usage: IF_INCLUDE_SUSv2( "\n" +//usage: "\n -s Print nothing, only return exit status" +//usage: ) +//usage: +//usage:#define tty_example_usage +//usage: "$ tty\n" +//usage: "/dev/tty2\n" + +#include "libbb.h" + +int tty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tty_main(int argc UNUSED_PARAM, char **argv) +{ + const char *s; + IF_INCLUDE_SUSv2(int silent;) /* Note: No longer relevant in SUSv3. */ + int retval; + + xfunc_error_retval = 2; /* SUSv3 requires > 1 for error. */ + + IF_INCLUDE_SUSv2(silent = getopt32(argv, "s");) + IF_INCLUDE_SUSv2(argv += optind;) + IF_NOT_INCLUDE_SUSv2(argv += 1;) + + /* gnu tty outputs a warning that it is ignoring all args. */ + bb_warn_ignoring_args(argv[0]); + + retval = EXIT_SUCCESS; + + s = xmalloc_ttyname(STDIN_FILENO); + if (s == NULL) { + /* According to SUSv3, ttyname can fail with EBADF or ENOTTY. + * We know the file descriptor is good, so failure means not a tty. */ + s = "not a tty"; + retval = EXIT_FAILURE; + } + IF_INCLUDE_SUSv2(if (!silent) puts(s);) + IF_NOT_INCLUDE_SUSv2(puts(s);) + + fflush_stdout_and_exit(retval); +} diff --git a/busybox/coreutils/uname.c b/busybox/coreutils/uname.c new file mode 100644 index 00000000000000..765809658a1e60 --- /dev/null +++ b/busybox/coreutils/uname.c @@ -0,0 +1,213 @@ +/* vi: set sw=4 ts=4: */ +/* + * uname -- print system information + * Copyright (C) 1989-1999 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Option Example + * -s, --sysname SunOS + * -n, --nodename rocky8 + * -r, --release 4.0 + * -v, --version + * -m, --machine sun + * -a, --all SunOS rocky8 4.0 sun + * + * The default behavior is equivalent to '-s'. + * + * David MacKenzie + * + * GNU coreutils 6.10: + * Option: struct Example(s): + * utsname + * field: + * -s, --kernel-name sysname Linux + * -n, --nodename nodename localhost.localdomain + * -r, --kernel-release release 2.6.29 + * -v, --kernel-version version #1 SMP Sun Jan 11 20:52:37 EST 2009 + * -m, --machine machine x86_64 i686 + * -p, --processor (none) x86_64 i686 + * -i, --hardware-platform (none) x86_64 i386 + * NB: vanilla coreutils reports "unknown" -p and -i, + * x86_64 and i686/i386 shown above are Fedora's inventions. + * -o, --operating-system (none) GNU/Linux + * -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 + * Further size reductions. + * Mar 16, 2003: Manuel Novoa III (mjn3@codepoet.org) + * Now does proper error checking on i/o. Plus some further space savings. + * Jan 2009: + * Fix handling of -a to not print "unknown", add -o and -i support. + */ +//config:config UNAME +//config: bool "uname (3.7 kb)" +//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". +//config: +//can't use "ARCH" for this applet, all hell breaks loose in build system :) +//config:config BB_ARCH +//config: bool "arch (1.6 kb)" +//config: default y +//config: help +//config: Same as uname -m. + +// APPLET_NOFORK:name main location suid_type help +//applet:IF_UNAME(APPLET_NOFORK( uname, uname, BB_DIR_BIN, BB_SUID_DROP, uname)) +//applet:IF_BB_ARCH(APPLET_NOFORK(arch, uname, BB_DIR_BIN, BB_SUID_DROP, arch)) + +//kbuild:lib-$(CONFIG_UNAME) += uname.o +//kbuild:lib-$(CONFIG_BB_ARCH) += uname.o + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */ + +//usage:#define uname_trivial_usage +//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 Kernel release" +//usage: "\n -s Kernel name (default)" +//usage: "\n -p Processor type" +//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" +//usage: "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n" + +//usage:#define arch_trivial_usage +//usage: "" +//usage:#define arch_full_usage "\n\n" +//usage: "Print system architecture" + +#include "libbb.h" +/* After libbb.h, since it needs sys/types.h on some systems */ +#include + +typedef struct { + struct utsname name; + char processor[sizeof(((struct utsname*)NULL)->machine)]; + char platform[sizeof(((struct utsname*)NULL)->machine)]; + char os[sizeof(CONFIG_UNAME_OSNAME)]; +} uname_info_t; + +#if ENABLE_UNAME +#define options "snrvmpioa" +static const unsigned short utsname_offset[] = { + offsetof(uname_info_t, name.sysname), /* -s */ + offsetof(uname_info_t, name.nodename), /* -n */ + offsetof(uname_info_t, name.release), /* -r */ + offsetof(uname_info_t, name.version), /* -v */ + offsetof(uname_info_t, name.machine), /* -m */ + offsetof(uname_info_t, processor), /* -p */ + offsetof(uname_info_t, platform), /* -i */ + offsetof(uname_info_t, os), /* -o */ +}; +#endif + +int uname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uname_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + uname_info_t uname_info; + IF_UNAME(const char *unknown_str = "unknown";) +#if ENABLE_UNAME + unsigned toprint = (1 << 4); /* "arch" = "uname -m" */ + + if (!ENABLE_BB_ARCH || applet_name[0] == 'u') { +# if ENABLE_LONG_OPTS + static const char uname_longopts[] ALIGN1 = + /* name, has_arg, val */ + "all\0" No_argument "a" + "kernel-name\0" No_argument "s" + "nodename\0" No_argument "n" + "kernel-release\0" No_argument "r" + "release\0" No_argument "r" + "kernel-version\0" No_argument "v" + "machine\0" No_argument "m" + "processor\0" No_argument "p" + "hardware-platform\0" No_argument "i" + "operating-system\0" No_argument "o" + ; +# endif + toprint = getopt32long(argv, options, uname_longopts); + if (argv[optind]) { /* coreutils-6.9 compat */ + bb_show_usage(); + } + if (toprint & (1 << 8)) { /* -a => all opts on */ + toprint = (1 << 8) - 1; + unknown_str = ""; /* -a does not print unknown fields */ + } + if (toprint == 0) { /* no opts => -s (sysname) */ + toprint = 1; + } + } /* if "uname" */ +#endif + + uname(&uname_info.name); /* never fails */ + +#if defined(__sparc__) && defined(__linux__) + { + char *fake_sparc = getenv("FAKE_SPARC"); + if (fake_sparc && (fake_sparc[0] | 0x20) == 'y') { + strcpy(uname_info.name.machine, "sparc"); + } + } +#endif + if (ENABLE_BB_ARCH && (!ENABLE_UNAME || applet_name[0] == 'a')) { + puts(uname_info.name.machine); + } else { +#if ENABLE_UNAME + /* "uname" */ + const char *fmt; + const unsigned short *delta; + + strcpy(uname_info.processor, unknown_str); + strcpy(uname_info.platform, unknown_str); + strcpy(uname_info.os, CONFIG_UNAME_OSNAME); +# if ENABLE_FEDORA_COMPAT + /* Fedora does something like this */ + strcpy(uname_info.processor, uname_info.name.machine); + strcpy(uname_info.platform, uname_info.name.machine); + if (uname_info.platform[0] == 'i' + && uname_info.platform[1] + && uname_info.platform[2] == '8' + && uname_info.platform[3] == '6' + ) { + uname_info.platform[1] = '3'; + } +# endif + delta = utsname_offset; + fmt = " %s" + 1; + do { + if (toprint & 1) { + const char *p = (char *)(&uname_info) + *delta; + if (p[0]) { + printf(fmt, p); + fmt = " %s"; + } + } + ++delta; + } while (toprint >>= 1); + bb_putchar('\n'); +#endif + } + + fflush_stdout_and_exit(EXIT_SUCCESS); /* coreutils-6.9 compat */ +} diff --git a/busybox/coreutils/uniq.c b/busybox/coreutils/uniq.c new file mode 100644 index 00000000000000..f71557b675081f --- /dev/null +++ b/busybox/coreutils/uniq.c @@ -0,0 +1,139 @@ +/* vi: set sw=4 ts=4: */ +/* + * uniq implementation for busybox + * + * Copyright (C) 2005 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config UNIQ +//config: bool "uniq (4.8 kb)" +//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 */ + +//usage:#define uniq_trivial_usage +//usage: "[-cdu][-f,s,w N] [INPUT [OUTPUT]]" +//usage:#define uniq_full_usage "\n\n" +//usage: "Discard duplicate lines\n" +//usage: "\n -c Prefix lines by the number of occurrences" +//usage: "\n -d Only print duplicate lines" +//usage: "\n -u Only print unique lines" +//usage: "\n -i Ignore case" +//usage: "\n -f N Skip first N fields" +//usage: "\n -s N Skip first N chars (after any skipped fields)" +//usage: "\n -w N Compare N characters in line" +//usage: +//usage:#define uniq_example_usage +//usage: "$ echo -e \"a\\na\\nb\\nc\\nc\\na\" | sort | uniq\n" +//usage: "a\n" +//usage: "b\n" +//usage: "c\n" + +#include "libbb.h" + +int uniq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uniq_main(int argc UNUSED_PARAM, char **argv) +{ + const char *input_filename; + unsigned skip_fields, skip_chars, max_chars; + unsigned opt; + char *cur_line; + const char *cur_compare; + + enum { + OPT_c = 0x1, + OPT_d = 0x2, /* print only dups */ + OPT_u = 0x4, /* print only uniq */ + OPT_f = 0x8, + OPT_s = 0x10, + OPT_w = 0x20, + OPT_i = 0x40, + }; + + skip_fields = skip_chars = 0; + max_chars = INT_MAX; + + opt = getopt32(argv, "cduf:+s:+w:+i", &skip_fields, &skip_chars, &max_chars); + argv += optind; + + input_filename = argv[0]; + if (input_filename) { + const char *output; + + if (input_filename[0] != '-' || input_filename[1]) { + close(STDIN_FILENO); /* == 0 */ + xopen(input_filename, O_RDONLY); /* fd will be 0 */ + } + output = argv[1]; + if (output) { + if (argv[2]) + bb_show_usage(); + if (output[0] != '-' || output[1]) { + // Won't work with "uniq - FILE" and closed stdin: + //close(STDOUT_FILENO); + //xopen(output, O_WRONLY | O_CREAT | O_TRUNC); + xmove_fd(xopen(output, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); + } + } + } + + cur_compare = cur_line = NULL; /* prime the pump */ + + do { + unsigned i; + unsigned long dups; + char *old_line; + const char *old_compare; + + old_line = cur_line; + old_compare = cur_compare; + dups = 0; + + /* gnu uniq ignores newlines */ + while ((cur_line = xmalloc_fgetline(stdin)) != NULL) { + cur_compare = cur_line; + for (i = skip_fields; i; i--) { + cur_compare = skip_whitespace(cur_compare); + cur_compare = skip_non_whitespace(cur_compare); + } + for (i = skip_chars; *cur_compare && i; i--) { + ++cur_compare; + } + + if (!old_line) + break; + if ((opt & OPT_i) + ? strncasecmp(old_compare, cur_compare, max_chars) + : strncmp(old_compare, cur_compare, max_chars) + ) { + break; + } + + free(cur_line); + ++dups; /* testing for overflow seems excessive */ + } + + if (old_line) { + if (!(opt & (OPT_d << !!dups))) { /* (if dups, opt & OPT_u) */ + if (opt & OPT_c) { + /* %7lu matches GNU coreutils 6.9 */ + printf("%7lu ", dups + 1); + } + puts(old_line); + } + free(old_line); + } + } while (cur_line); + + die_if_ferror(stdin, input_filename); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/unlink.c b/busybox/coreutils/unlink.c new file mode 100644 index 00000000000000..61b108a84f42e3 --- /dev/null +++ b/busybox/coreutils/unlink.c @@ -0,0 +1,33 @@ +/* vi: set sw=4 ts=4: */ +/* + * unlink for busybox + * + * Copyright (C) 2014 Isaac Dunham + * + * Licensed under GPLv2, see LICENSE in this source tree + */ +//config:config UNLINK +//config: bool "unlink (3.5 kb)" +//config: default y +//config: help +//config: unlink deletes a file by calling unlink() + +//applet:IF_UNLINK(APPLET_NOFORK(unlink, unlink, BB_DIR_USR_BIN, BB_SUID_DROP, unlink)) + +//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) +{ + getopt32(argv, "^" "" "\0" "=1"); + argv += optind; + xunlink(argv[0]); + return 0; +} diff --git a/busybox/coreutils/usleep.c b/busybox/coreutils/usleep.c new file mode 100644 index 00000000000000..684ab781b179ec --- /dev/null +++ b/busybox/coreutils/usleep.c @@ -0,0 +1,51 @@ +/* vi: set sw=4 ts=4: */ +/* + * usleep implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config USLEEP +//config: bool "usleep (1.1 kb)" +//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. */ + +//usage:#define usleep_trivial_usage +//usage: "N" +//usage:#define usleep_full_usage "\n\n" +//usage: "Pause for N microseconds" +//usage: +//usage:#define usleep_example_usage +//usage: "$ usleep 1000000\n" +//usage: "[pauses for 1 second]\n" + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int usleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int usleep_main(int argc UNUSED_PARAM, char **argv) +{ + if (!argv[1]) { + bb_show_usage(); + } + + /* Safe wrt NOFORK? (noforks are not allowed to run for + * a long time). Try "usleep 99999999" + ^C + "echo $?" + * in hush with FEATURE_SH_NOFORK=y. + * At least on uclibc, usleep() thanslates to nanosleep() + * which returns early on any signal (even caught one), + * and uclibc does not loop back on EINTR. + */ + usleep(xatou(argv[1])); + + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/uudecode.c b/busybox/coreutils/uudecode.c new file mode 100644 index 00000000000000..5ef05ee4d6beb6 --- /dev/null +++ b/busybox/coreutils/uudecode.c @@ -0,0 +1,263 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 2003, Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Based on specification from + * http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html + * + * Bugs: the spec doesn't mention anything about "`\n`\n" prior to the + * "end" line + */ +//config:config UUDECODE +//config: bool "uudecode (5.9 kb)" +//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]" +//usage:#define uudecode_full_usage "\n\n" +//usage: "Uudecode a file\n" +//usage: "Finds OUTFILE in uuencoded source unless -o is given" +//usage: +//usage:#define uudecode_example_usage +//usage: "$ uudecode -o busybox busybox.uu\n" +//usage: "$ ls -l busybox\n" +//usage: "-rwxr-xr-x 1 ams ams 245264 Jun 7 21:35 busybox\n" + +#include "libbb.h" + +#if ENABLE_UUDECODE +static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags UNUSED_PARAM) +{ + char *line; + + 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. + * Note: space should not be trimmed, some encoders use it instead of "`" + * for padding of last incomplete 4-char block. + */ + str_len = line_len; + while (--str_len >= 0 + && (line[str_len] == '\n' || line[str_len] == '\r') + ) { + line[str_len] = '\0'; + } + + if (strcmp(line, "end") == 0) { + return; /* the only non-error exit */ + } + + line_ptr = line; + while (*line_ptr) { + *line_ptr = (*line_ptr - 0x20) & 0x3f; + line_ptr++; + } + str_len = line_ptr - line; + + encoded_len = line[0] * 4 / 3; + /* Check that line is not too short. (we tolerate + * 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"); */ + } + if (encoded_len <= 0) { + /* Ignore the "`\n" line, why is it even in the encode file ? */ + free(line); + continue; + } + if (encoded_len > 60) { + bb_error_msg_and_die("line too long"); + } + + dst = line; + line_ptr = line + 1; + do { + /* Merge four 6 bit chars to three 8 bit chars */ + *dst++ = line_ptr[0] << 2 | line_ptr[1] >> 4; + encoded_len--; + if (encoded_len == 0) { + break; + } + + *dst++ = line_ptr[1] << 4 | line_ptr[2] >> 2; + encoded_len--; + if (encoded_len == 0) { + break; + } + + *dst++ = line_ptr[2] << 6 | line_ptr[3]; + line_ptr += 4; + encoded_len -= 2; + } while (encoded_len > 0); + fwrite(line, 1, dst - line, dst_stream); + free(line); + } + bb_error_msg_and_die("short file"); +} +#endif + +#if ENABLE_UUDECODE +int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uudecode_main(int argc UNUSED_PARAM, char **argv) +{ + FILE *src_stream; + char *outname = NULL; + char *line; + + getopt32(argv, "^" "o:" "\0" "?1"/* 1 arg max*/, &outname); + argv += optind; + + if (!argv[0]) + *--argv = (char*)"-"; + src_stream = xfopen_stdin(argv[0]); + + /* Search for the start of the encoding */ + while ((line = xmalloc_fgetline(src_stream)) != NULL) { + void FAST_FUNC (*decode_fn_ptr)(FILE *src, FILE *dst, int flags); + char *line_ptr; + FILE *dst_stream; + int mode; + + if (is_prefixed_with(line, "begin-base64 ")) { + line_ptr = line + 13; + decode_fn_ptr = read_base64; + } else if (is_prefixed_with(line, "begin ")) { + line_ptr = line + 6; + decode_fn_ptr = read_stduu; + } else { + free(line); + continue; + } + + /* begin line found. decode and exit */ + mode = bb_strtou(line_ptr, NULL, 8); + if (outname == NULL) { + outname = strchr(line_ptr, ' '); + if (!outname) + break; + outname++; + trim(outname); /* remove trailing space (and '\r' for DOS text) */ + if (!outname[0]) + break; + } + dst_stream = stdout; + if (NOT_LONE_DASH(outname)) { + dst_stream = xfopen_for_write(outname); + fchmod(fileno(dst_stream), mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + } + free(line); + decode_fn_ptr(src_stream, dst_stream, /*flags:*/ BASE64_FLAG_UU_STOP + BASE64_FLAG_NO_STOP_CHAR); + /* fclose_if_not_stdin(src_stream); - redundant */ + return EXIT_SUCCESS; + } + bb_error_msg_and_die("no 'begin' line"); +} +#endif + +//applet:IF_BASE64(APPLET(base64, BB_DIR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_BASE64) += uudecode.o + +//config:config BASE64 +//config: bool "base64 (5 kb)" +//config: default y +//config: help +//config: Base64 encode and decode + +//usage:#define base64_trivial_usage +//usage: "[-d] [FILE]" +//usage:#define base64_full_usage "\n\n" +//usage: "Base64 encode or decode FILE to standard output" +//usage: "\n -d Decode data" +////usage: "\n -w COL Wrap lines at COL (default 76, 0 disables)" +////usage: "\n -i When decoding, ignore non-alphabet characters" + +#if ENABLE_BASE64 +int base64_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int base64_main(int argc UNUSED_PARAM, char **argv) +{ + FILE *src_stream; + unsigned opts; + + opts = getopt32(argv, "^" "d" "\0" "?1"/* 1 arg max*/); + argv += optind; + + if (!argv[0]) + *--argv = (char*)"-"; + src_stream = xfopen_stdin(argv[0]); + if (opts) { + read_base64(src_stream, stdout, /*flags:*/ (char)EOF); + } else { + enum { + SRC_BUF_SIZE = 76/4*3, /* This *MUST* be a multiple of 3 */ + DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), + }; + char src_buf[SRC_BUF_SIZE]; + char dst_buf[DST_BUF_SIZE + 1]; + int src_fd = fileno(src_stream); + while (1) { + size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE); + if (!size) + break; + if ((ssize_t)size < 0) + bb_perror_msg_and_die(bb_msg_read_error); + /* Encode the buffer we just read in */ + bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64); + xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3)); + bb_putchar('\n'); + fflush(stdout); + } + } + + fflush_stdout_and_exit(EXIT_SUCCESS); +} +#endif + +/* Test script. +Put this into an empty dir with busybox binary, an run. + +#!/bin/sh +test -x busybox || { echo "No ./busybox?"; exit; } +ln -sf busybox uudecode +ln -sf busybox uuencode +>A_null +echo -n A >A +echo -n AB >AB +echo -n ABC >ABC +echo -n ABCD >ABCD +echo -n ABCDE >ABCDE +echo -n ABCDEF >ABCDEF +cat busybox >A_bbox +for f in A*; do + echo uuencode $f + ./uuencode $f <$f >u_$f + ./uuencode -m $f <$f >m_$f +done +mkdir unpk_u unpk_m 2>/dev/null +for f in u_*; do + ./uudecode <$f -o unpk_u/${f:2} + diff -a ${f:2} unpk_u/${f:2} >/dev/null 2>&1 + echo uudecode $f: $? +done +for f in m_*; do + ./uudecode <$f -o unpk_m/${f:2} + diff -a ${f:2} unpk_m/${f:2} >/dev/null 2>&1 + echo uudecode $f: $? +done +*/ diff --git a/busybox/coreutils/uuencode.c b/busybox/coreutils/uuencode.c new file mode 100644 index 00000000000000..aa53b14a64fb10 --- /dev/null +++ b/busybox/coreutils/uuencode.c @@ -0,0 +1,82 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2000 by Glenn McGrath + * + * based on the function base64_encode from http.c in wget v1.6 + * Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config UUENCODE +//config: bool "uuencode (4.6 kb)" +//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" +//usage:#define uuencode_full_usage "\n\n" +//usage: "Uuencode FILE (or stdin) to stdout\n" +//usage: "\n -m Use base64 encoding per RFC1521" +//usage: +//usage:#define uuencode_example_usage +//usage: "$ uuencode busybox busybox\n" +//usage: "begin 755 busybox\n" +//usage: "\n" +//usage: "$ uudecode busybox busybox > busybox.uu\n" +//usage: "$\n" + +#include "libbb.h" + +enum { + SRC_BUF_SIZE = 15*3, /* This *MUST* be a multiple of 3 */ + DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), +}; + +int uuencode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uuencode_main(int argc UNUSED_PARAM, char **argv) +{ + struct stat stat_buf; + int src_fd = STDIN_FILENO; + const char *tbl; + mode_t mode; + char src_buf[SRC_BUF_SIZE]; + char dst_buf[DST_BUF_SIZE + 1]; + + tbl = bb_uuenc_tbl_std; + mode = 0666 & ~umask(0666); + if (getopt32(argv, "^" "m" "\0" "-1:?2"/*must have 1 or 2 args*/)) { + tbl = bb_uuenc_tbl_base64; + } + argv += optind; + if (argv[1]) { + src_fd = xopen(argv[0], O_RDONLY); + fstat(src_fd, &stat_buf); + mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + argv++; + } + + printf("begin%s %o %s", tbl == bb_uuenc_tbl_std ? "" : "-base64", mode, *argv); + while (1) { + size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE); + if (!size) + break; + if ((ssize_t)size < 0) + bb_perror_msg_and_die(bb_msg_read_error); + /* Encode the buffer we just read in */ + bb_uuencode(dst_buf, src_buf, size, tbl); + bb_putchar('\n'); + if (tbl == bb_uuenc_tbl_std) { + bb_putchar(tbl[size]); + } + fflush(stdout); + xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3)); + } + printf(tbl == bb_uuenc_tbl_std ? "\n`\nend\n" : "\n====\n"); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/busybox/coreutils/wc.c b/busybox/coreutils/wc.c new file mode 100644 index 00000000000000..c74b7a65f35f43 --- /dev/null +++ b/busybox/coreutils/wc.c @@ -0,0 +1,257 @@ +/* vi: set sw=4 ts=4: */ +/* + * wc implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Rewritten to fix a number of problems and do some size optimizations. + * Problems in the previous busybox implementation (besides bloat) included: + * 1) broken 'wc -c' optimization (read note below) + * 2) broken handling of '-' args + * 3) no checking of ferror on EOF returns + * 4) isprint() wasn't considered when word counting. + * + * NOTES: + * + * The previous busybox wc attempted an optimization using stat for the + * case of counting chars only. I omitted that because it was broken. + * It didn't take into account the possibility of input coming from a + * pipe, or input from a file with file pointer not at the beginning. + * + * To implement such a speed optimization correctly, not only do you + * need the size, but also the file position. Note also that the + * file position may be past the end of file. Consider the example + * (adapted from example in gnu wc.c) + * + * echo hello > /tmp/testfile && + * (dd ibs=1k skip=1 count=0 &> /dev/null; wc -c) < /tmp/testfile + * + * for which 'wc -c' should output '0'. + */ +//config:config WC +//config: bool "wc (4.4 kb)" +//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 counts" +//config: default y +//config: depends on WC +//config: help +//config: Use "unsigned long long" 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" + +#if !ENABLE_LOCALE_SUPPORT +# undef isprint +# undef isspace +# define isprint(c) ((unsigned)((c) - 0x20) <= (0x7e - 0x20)) +# define isspace(c) ((c) == ' ') +#endif + +#if ENABLE_FEATURE_WC_LARGE +# define COUNT_T unsigned long long +# define COUNT_FMT "llu" +#else +# define COUNT_T unsigned +# define COUNT_FMT "u" +#endif + +/* We support -m even when UNICODE_SUPPORT is off, + * we just don't advertise it in help text, + * since it is the same as -c in this case. + */ + +//usage:#define wc_trivial_usage +//usage: "[-c"IF_UNICODE_SUPPORT("m")"lwL] [FILE]..." +//usage: +//usage:#define wc_full_usage "\n\n" +//usage: "Count lines, words, and bytes for each FILE (or stdin)\n" +//usage: "\n -c Count bytes" +//usage: IF_UNICODE_SUPPORT( +//usage: "\n -m Count characters" +//usage: ) +//usage: "\n -l Count newlines" +//usage: "\n -w Count words" +//usage: "\n -L Print longest line length" +//usage: +//usage:#define wc_example_usage +//usage: "$ wc /etc/passwd\n" +//usage: " 31 46 1365 /etc/passwd\n" + +/* Order is important if we want to be compatible with + * column order in "wc -cmlwL" output: + */ +enum { + WC_LINES = 0, /* -l */ + WC_WORDS = 1, /* -w */ + WC_UNICHARS = 2, /* -m */ + WC_BYTES = 3, /* -c */ + WC_LENGTH = 4, /* -L */ + NUM_WCS = 5, +}; + +int wc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int wc_main(int argc UNUSED_PARAM, char **argv) +{ + const char *arg; + const char *start_fmt = " %9"COUNT_FMT + 1; + const char *fname_fmt = " %s\n"; + COUNT_T *pcounts; + COUNT_T counts[NUM_WCS]; + COUNT_T totals[NUM_WCS]; + int num_files; + smallint status = EXIT_SUCCESS; + unsigned print_type; + + init_unicode(); + + print_type = getopt32(argv, "lwmcL"); + + if (print_type == 0) { + print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_BYTES); + } + + argv += optind; + if (!argv[0]) { + *--argv = (char *) bb_msg_standard_input; + fname_fmt = "\n"; + } + if (!argv[1]) { /* zero or one filename? */ + if (!((print_type-1) & print_type)) /* exactly one option? */ + start_fmt = "%"COUNT_FMT; + } + + memset(totals, 0, sizeof(totals)); + + pcounts = counts; + + num_files = 0; + while ((arg = *argv++) != NULL) { + FILE *fp; + const char *s; + unsigned u; + unsigned linepos; + smallint in_word; + + ++num_files; + fp = fopen_or_warn_stdin(arg); + if (!fp) { + status = EXIT_FAILURE; + continue; + } + + memset(counts, 0, sizeof(counts)); + linepos = 0; + in_word = 0; + + while (1) { + int c; + /* Our -w doesn't match GNU wc exactly... oh well */ + + c = getc(fp); + if (c == EOF) { + if (ferror(fp)) { + bb_simple_perror_msg(arg); + status = EXIT_FAILURE; + } + goto DO_EOF; /* Treat an EOF as '\r'. */ + } + + /* Cater for -c and -m */ + ++counts[WC_BYTES]; + if (unicode_status != UNICODE_ON /* every byte is a new char */ + || (c & 0xc0) != 0x80 /* it isn't a 2nd+ byte of a Unicode char */ + ) { + ++counts[WC_UNICHARS]; + } + + if (isprint_asciionly(c)) { /* FIXME: not unicode-aware */ + ++linepos; + if (!isspace(c)) { + in_word = 1; + continue; + } + } else if ((unsigned)(c - 9) <= 4) { + /* \t 9 + * \n 10 + * \v 11 + * \f 12 + * \r 13 + */ + if (c == '\t') { + linepos = (linepos | 7) + 1; + } else { /* '\n', '\r', '\f', or '\v' */ + DO_EOF: + if (linepos > counts[WC_LENGTH]) { + counts[WC_LENGTH] = linepos; + } + if (c == '\n') { + ++counts[WC_LINES]; + } + if (c != '\v') { + linepos = 0; + } + } + } else { + continue; + } + + counts[WC_WORDS] += in_word; + in_word = 0; + if (c == EOF) { + break; + } + } + + fclose_if_not_stdin(fp); + + if (totals[WC_LENGTH] < counts[WC_LENGTH]) { + totals[WC_LENGTH] = counts[WC_LENGTH]; + } + totals[WC_LENGTH] -= counts[WC_LENGTH]; + + OUTPUT: + /* coreutils wc tries hard to print pretty columns + * (saves results for all files, finds max col len etc...) + * we won't try that hard, it will bloat us too much */ + s = start_fmt; + u = 0; + do { + if (print_type & (1 << u)) { + printf(s, pcounts[u]); + s = " %9"COUNT_FMT; /* Ok... restore the leading space. */ + } + totals[u] += pcounts[u]; + } while (++u < NUM_WCS); + printf(fname_fmt, arg); + } + + /* If more than one file was processed, we want the totals. To save some + * space, we set the pcounts ptr to the totals array. This has the side + * effect of trashing the totals array after outputting it, but that's + * irrelavent since we no longer need it. */ + if (num_files > 1) { + num_files = 0; /* Make sure we don't get here again. */ + arg = "total"; + pcounts = totals; + --argv; + goto OUTPUT; + } + + fflush_stdout_and_exit(status); +} diff --git a/busybox/coreutils/who.c b/busybox/coreutils/who.c new file mode 100644 index 00000000000000..80226c3d6a74b8 --- /dev/null +++ b/busybox/coreutils/who.c @@ -0,0 +1,170 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini who is used to display user name, login time, + * idle time and host name. + * + * Author: Da Chen + * + * This is a free document; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation: + * http://www.gnu.org/copyleft/gpl.html + * + * Copyright (c) 2002 AYR Networks, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config WHO +//config: bool "who (3.7 kb)" +//config: default y +//config: depends on FEATURE_UTMP +//config: help +//config: Print users currently logged on. +//config: +// procps-ng has this variation of "who": +//config:config W +//config: bool "w (3.7 kb)" +//config: default y +//config: depends on FEATURE_UTMP +//config: help +//config: Print users currently logged on. +//config: +//config:config USERS +//config: bool "users (3.2 kb)" +//config: default y +//config: depends on FEATURE_UTMP +//config: help +//config: Print users currently logged on. + +// APPLET_NOEXEC:name main location suid_type help +//applet:IF_USERS(APPLET_NOEXEC(users, who, BB_DIR_USR_BIN, BB_SUID_DROP, users)) +//applet:IF_W( APPLET_NOEXEC(w, who, BB_DIR_USR_BIN, BB_SUID_DROP, w)) +//applet:IF_WHO( APPLET_NOEXEC(who, who, BB_DIR_USR_BIN, BB_SUID_DROP, who)) + +//kbuild:lib-$(CONFIG_USERS) += who.o +//kbuild:lib-$(CONFIG_W) += 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" +//usage: "Print the users currently logged on" + +//usage:#define w_trivial_usage +//usage: "" +//usage:#define w_full_usage "\n\n" +//usage: "Show who is logged on" +// +// procps-ng 3.3.10: +// "\n -h, --no-header" +// "\n -u, --no-current" +// Ignores the username while figuring out the current process +// and cpu times. To demonstrate this, do a "su" and do a "w" and a "w -u". +// "\n -s, --short" +// Short format. Don't print the login time, JCPU or PCPU times. +// "\n -f, --from" +// Toggle printing the from (remote hostname) field. +// The default is for the from field to not be printed +// "\n -i, --ip-addr" +// Display IP address instead of hostname for from field. +// "\n -o, --old-style" +// Old style output. Prints blank space for idle times less than one minute. +// Example output: +// 17:28:00 up 4 days, 22:41, 4 users, load average: 0.84, 0.97, 0.90 +// USER TTY LOGIN@ IDLE JCPU PCPU WHAT +// root tty1 Thu18 4days 4:33m 0.07s /bin/sh /etc/xdg/xfce4/xinitrc -- vt +// root pts/1 Mon13 3:24m 1:01 0.01s w + +//usage:#define who_trivial_usage +//usage: "[-a]" +//usage:#define who_full_usage "\n\n" +//usage: "Show who is logged on\n" +//usage: "\n -a Show all" +//usage: "\n -H Print column headers" + +#include "libbb.h" + +static void idle_string(char *str6, time_t t) +{ + t = time(NULL) - t; + + /*if (t < 60) { + str6[0] = '.'; + str6[1] = '\0'; + return; + }*/ + if (t >= 0 && t < (24 * 60 * 60)) { + sprintf(str6, "%02d:%02d", + (int) (t / (60 * 60)), + (int) ((t % (60 * 60)) / 60)); + return; + } + strcpy(str6, "old"); +} + +int who_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int who_main(int argc UNUSED_PARAM, char **argv) +{ +#define CNT_APPLET (ENABLE_USERS + ENABLE_W + ENABLE_WHO) + int do_users = (ENABLE_USERS && (CNT_APPLET == 1 || applet_name[0] == 'u')); + int do_w = (ENABLE_W && (CNT_APPLET == 1 || applet_name[1] == '\0')); + int do_who = (ENABLE_WHO && (CNT_APPLET == 1 || applet_name[1] == 'h')); + struct utmpx *ut; + unsigned opt; + const char *fmt = "%s"; + + opt = getopt32(argv, do_who ? "^" "aH" "\0" "=0": "^" "" "\0" "=0"); + if ((opt & 2) || do_w) /* -H or we are w */ + puts("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST"); + + setutxent(); + while ((ut = getutxent()) != NULL) { + if (ut->ut_user[0] + && ((opt & 1) || ut->ut_type == USER_PROCESS) + ) { + if (!do_users) { + char str6[6]; + char name[sizeof("/dev/") + sizeof(ut->ut_line) + 1]; + struct stat st; + time_t seconds; + + str6[0] = '?'; + str6[1] = '\0'; + strcpy(name, "/dev/"); + safe_strncpy(ut->ut_line[0] == '/' ? name : name + sizeof("/dev/")-1, + ut->ut_line, + sizeof(ut->ut_line)+1 + ); + if (stat(name, &st) == 0) + idle_string(str6, st.st_atime); + /* manpages say ut_tv.tv_sec *is* time_t, + * but some systems have it wrong */ + seconds = ut->ut_tv.tv_sec; + /* How wide time field can be? + * "Nov 10 19:33:20": 15 chars + * "2010-11-10 19:33": 16 chars + */ + printf("%-15.*s %-15.*s %-7s %-16.16s %.*s\n", + (int)sizeof(ut->ut_user), ut->ut_user, + (int)sizeof(ut->ut_line), ut->ut_line, + str6, +// TODO: with LANG=en_US.UTF-8, who from coreutils 8.25 shows +// TIME col as "2017-04-06 18:47" (the default format is "Apr 6 18:47"). +// The former format looks saner to me. Switch to it unconditionally? + ctime(&seconds) + 4, + (int)sizeof(ut->ut_host), ut->ut_host + ); + } else { + printf(fmt, ut->ut_user); + fmt = " %s"; + } + } + } + if (do_users) + bb_putchar('\n'); + if (ENABLE_FEATURE_CLEAN_UP) + endutxent(); + return EXIT_SUCCESS; +} diff --git a/busybox/coreutils/whoami.c b/busybox/coreutils/whoami.c new file mode 100644 index 00000000000000..9dab15817d0d25 --- /dev/null +++ b/busybox/coreutils/whoami.c @@ -0,0 +1,39 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini whoami implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config WHOAMI +//config: bool "whoami (2.9 kb)" +//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. */ + +//usage:#define whoami_trivial_usage +//usage: "" +//usage:#define whoami_full_usage "\n\n" +//usage: "Print the user name associated with the current effective user id" + +#include "libbb.h" + +int whoami_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int whoami_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + if (argv[1]) + bb_show_usage(); + + /* Will complain and die if username not found */ + puts(xuid2uname(geteuid())); + + return fflush_all(); +} diff --git a/busybox/coreutils/yes.c b/busybox/coreutils/yes.c new file mode 100644 index 00000000000000..c244bfe102817b --- /dev/null +++ b/busybox/coreutils/yes.c @@ -0,0 +1,54 @@ +/* vi: set sw=4 ts=4: */ +/* + * yes implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* 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 (956 bytes)" +//config: default y +//config: help +//config: yes is used to repeatedly output a specific string, or +//config: the default string 'y'. + +//applet:IF_YES(APPLET_NOEXEC(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes)) +/* was NOFORK, but then yes can't be ^C'ed if run by hush */ + +//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) +{ + char **pp; + + argv[0] = (char*)"y"; + if (argv[1]) + ++argv; + + do { + pp = argv; + while (1) { + fputs(*pp, stdout); + if (!*++pp) + break; + putchar(' '); + } + } while (putchar('\n') != EOF); + + bb_perror_nomsg_and_die(); +} diff --git a/busybox/debianutils/Config.src b/busybox/debianutils/Config.src new file mode 100644 index 00000000000000..17b0d89453ff8c --- /dev/null +++ b/busybox/debianutils/Config.src @@ -0,0 +1,10 @@ +# +# For a description of the syntax of this configuration file, +# see docs/Kconfig-language.txt. +# + +menu "Debian Utilities" + +INSERT + +endmenu diff --git a/busybox/debianutils/Kbuild.src b/busybox/debianutils/Kbuild.src new file mode 100644 index 00000000000000..6b4fb747007c3f --- /dev/null +++ b/busybox/debianutils/Kbuild.src @@ -0,0 +1,9 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +lib-y:= + +INSERT diff --git a/busybox/debianutils/pipe_progress.c b/busybox/debianutils/pipe_progress.c new file mode 100644 index 00000000000000..e6b7601faee719 --- /dev/null +++ b/busybox/debianutils/pipe_progress.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * Monitor a pipe with a simple progress display. + * + * Copyright (C) 2003 by Rob Landley , Joey Hess + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config PIPE_PROGRESS +//config: bool "pipe_progress (225 bytes)" +//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 "" + +#include "libbb.h" + +#define PIPE_PROGRESS_SIZE 4096 + +/* Read a block of data from stdin, write it to stdout. + * Activity is indicated by a '.' to stderr + */ +int pipe_progress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int pipe_progress_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + char buf[PIPE_PROGRESS_SIZE]; + time_t t = time(NULL); + int len; + + while ((len = safe_read(STDIN_FILENO, buf, PIPE_PROGRESS_SIZE)) > 0) { + time_t new_time = time(NULL); + if (new_time != t) { + t = new_time; + bb_putchar_stderr('.'); + } + full_write(STDOUT_FILENO, buf, len); + } + + bb_putchar_stderr('\n'); + + return 0; +} diff --git a/busybox/debianutils/run_parts.c b/busybox/debianutils/run_parts.c new file mode 100644 index 00000000000000..e4d61df359014c --- /dev/null +++ b/busybox/debianutils/run_parts.c @@ -0,0 +1,235 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini run-parts implementation for busybox + * + * Copyright (C) 2007 Bernhard Reutner-Fischer + * + * Based on a older version that was in busybox which was 1k big. + * Copyright (C) 2001 by Emanuele Aina + * + * Based on the Debian run-parts program, version 1.15 + * Copyright (C) 1996 Jeff Noxon , + * Copyright (C) 1996-1999 Guy Maor + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* This is my first attempt to write a program in C (well, this is my first + * attempt to write a program! :-) . */ + +/* This piece of code is heavily based on the original version of run-parts, + * taken from debian-utils. I've only removed the long options and the + * 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 (5.6 kb)" +//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: +//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] " +//usage: IF_FEATURE_RUN_PARTS_LONG_OPTIONS("[--reverse] [--test] [--exit-on-error] "IF_FEATURE_RUN_PARTS_FANCY("[--list] ")) +//usage: "DIRECTORY" +//usage:#define run_parts_full_usage "\n\n" +//usage: "Run a bunch of scripts in DIRECTORY\n" +//usage: "\n -a ARG Pass ARG as argument to scripts" +//usage: "\n -u UMASK Set UMASK before running scripts" +//usage: IF_FEATURE_RUN_PARTS_LONG_OPTIONS( +//usage: "\n --reverse Reverse execution order" +//usage: "\n --test Dry run" +//usage: "\n --exit-on-error Exit if a script exits with non-zero" +//usage: IF_FEATURE_RUN_PARTS_FANCY( +//usage: "\n --list Print names of matching files even if they are not executable" +//usage: ) +//usage: ) +//usage: +//usage:#define run_parts_example_usage +//usage: "$ run-parts -a start /etc/init.d\n" +//usage: "$ run-parts -a stop=now /etc/init.d\n\n" +//usage: "Let's assume you have a script foo/dosomething:\n" +//usage: "#!/bin/sh\n" +//usage: "for i in $*; do eval $i; done; unset i\n" +//usage: "case \"$1\" in\n" +//usage: "start*) echo starting something;;\n" +//usage: "stop*) set -x; shutdown -h $stop;;\n" +//usage: "esac\n\n" +//usage: "Running this yields:\n" +//usage: "$run-parts -a stop=+4m foo/\n" +//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 names (G.names) +#define cur (G.cur ) +#define cmd (G.cmd ) +#define INIT_G() do { setup_common_bufsiz(); } while (0) + +enum { NUM_CMD = (COMMON_BUFSIZE - sizeof(G)) / sizeof(cmd[0]) - 1 }; + +enum { + OPT_a = (1 << 0), + OPT_u = (1 << 1), + OPT_r = (1 << 2) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS, + OPT_t = (1 << 3) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS, + OPT_e = (1 << 4) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS, + OPT_l = (1 << 5) * ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS + * ENABLE_FEATURE_RUN_PARTS_FANCY, +}; + +/* Is this a valid filename (upper/lower alpha, digits, + * underscores, and hyphens only?) + */ +static bool invalid_name(const char *c) +{ + c = bb_basename(c); + + while (*c && (isalnum(*c) || *c == '_' || *c == '-')) + c++; + + return *c; /* TRUE (!0) if terminating NUL is not reached */ +} + +static int bb_alphasort(const void *p1, const void *p2) +{ + int r = strcmp(*(char **) p1, *(char **) p2); + return (option_mask32 & OPT_r) ? -r : r; +} + +static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUSED_PARAM, int depth) +{ + if (depth == 1) + return TRUE; + + if (depth == 2 + && ( !(statbuf->st_mode & (S_IFREG | S_IFLNK)) + || invalid_name(file) + || (!(option_mask32 & OPT_l) && access(file, X_OK) != 0)) + ) { + return SKIP; + } + + names = xrealloc_vector(names, 4, cur); + names[cur++] = xstrdup(file); + /*names[cur] = NULL; - xrealloc_vector did it */ + + return TRUE; +} + +#if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS +static const char runparts_longopts[] ALIGN1 = + "arg\0" Required_argument "a" + "umask\0" Required_argument "u" +//TODO: "verbose\0" No_argument "v" + "reverse\0" No_argument "\xf0" + "test\0" No_argument "\xf1" + "exit-on-error\0" No_argument "\xf2" +# if ENABLE_FEATURE_RUN_PARTS_FANCY + "list\0" No_argument "\xf3" +# endif + ; +# define GETOPT32 getopt32long +# define LONGOPTS ,runparts_longopts +#else +# define GETOPT32 getopt32 +# define LONGOPTS +#endif + +int run_parts_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int run_parts_main(int argc UNUSED_PARAM, char **argv) +{ + const char *umask_p = "22"; + llist_t *arg_list = NULL; + unsigned n; + int ret; + + INIT_G(); + + /* We require exactly one argument: the directory name */ + GETOPT32(argv, "^" "a:*u:" "\0" "=1" LONGOPTS, + &arg_list, &umask_p + ); + + umask(xstrtou_range(umask_p, 8, 0, 07777)); + + n = 1; + while (arg_list && n < NUM_CMD) { + cmd[n++] = llist_pop(&arg_list); + } + /* cmd[n] = NULL; - is already zeroed out */ + + /* run-parts has to sort executables by name before running them */ + + recursive_action(argv[optind], + ACTION_RECURSE|ACTION_FOLLOWLINKS, + act, /* file action */ + act, /* dir action */ + NULL, /* user data */ + 1 /* depth */ + ); + + if (!names) + return 0; + + qsort(names, cur, sizeof(char *), bb_alphasort); + + n = 0; + while (1) { + char *name = *names++; + if (!name) + break; + if (option_mask32 & (OPT_t | OPT_l)) { + puts(name); + continue; + } + cmd[0] = name; + ret = spawn_and_wait(cmd); + if (ret == 0) + continue; + n = 1; + if (ret < 0) + bb_perror_msg("can't execute '%s'", name); + else /* ret > 0 */ + bb_error_msg("%s: exit status %u", name, ret & 0xff); + + if (option_mask32 & OPT_e) + xfunc_die(); + } + + return n; +} diff --git a/busybox/debianutils/start_stop_daemon.c b/busybox/debianutils/start_stop_daemon.c new file mode 100644 index 00000000000000..43b6fca2604291 --- /dev/null +++ b/busybox/debianutils/start_stop_daemon.c @@ -0,0 +1,539 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini start-stop-daemon implementation(s) for busybox + * + * Written by Marek Michalkiewicz , + * Adapted for busybox David Kimdon + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +/* +This is how it is supposed to work: + +start-stop-daemon [OPTIONS] [--start|--stop] [[--] arguments...] + +One (only) of these must be given: + -S,--start Start + -K,--stop Stop + +Search for matching processes. +If --stop is given, stop all matching processes (by sending a signal). +If --start is given, start a new process unless a matching process was found. + +Options controlling process matching +(if multiple conditions are specified, all must match): + -u,--user USERNAME|UID Only consider this user's processes + -n,--name PROCESS_NAME Look for processes by matching PROCESS_NAME + with comm field in /proc/$PID/stat. + Only basename is compared: + "ntpd" == "./ntpd" == "/path/to/ntpd". +[TODO: can PROCESS_NAME be a full pathname? Should we require full match then +with /proc/$PID/exe or argv[0] (comm can't be matched, it never contains path)] + -x,--exec EXECUTABLE Look for processes that were started with this + command in /proc/$PID/exe and /proc/$PID/cmdline + (/proc/$PID/cmdline is a bbox extension) + Unlike -n, we match against the full path: + "ntpd" != "./ntpd" != "/path/to/ntpd" + -p,--pidfile PID_FILE Look for processes with PID from this file + +Options which are valid for --start only: + -x,--exec EXECUTABLE Program to run (1st arg of execvp). Mandatory. + -a,--startas NAME argv[0] (defaults to EXECUTABLE) + -b,--background Put process into background + -N,--nicelevel N Add N to process' nice level + -c,--chuid USER[:[GRP]] Change to specified user [and group] + -m,--make-pidfile Write PID to the pidfile + (both -m and -p must be given!) + +Options which are valid for --stop only: + -s,--signal SIG Signal to send (default:TERM) + -t,--test Exit with status 0 if process is found + (we don't actually start or stop daemons) + +Misc options: + -o,--oknodo Exit with status 0 if nothing is done + -q,--quiet Quiet + -v,--verbose Verbose +*/ +//config:config START_STOP_DAEMON +//config: bool "start-stop-daemon (12 kb)" +//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_LONG_OPTIONS +//config: bool "Enable long options" +//config: default y +//config: depends on START_STOP_DAEMON && LONG_OPTS +//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: -o|--oknodo ignored since we exit with 0 anyway +//config: -v|--verbose +//config: -N|--nicelevel N + +//applet:IF_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, BB_DIR_SBIN, BB_SUID_DROP, start_stop_daemon)) +/* not NOEXEC: uses bb_common_bufsiz1 */ + +//kbuild:lib-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o + +//usage:#define start_stop_daemon_trivial_usage +//usage: "[OPTIONS] [-S|-K] ... [-- ARGS...]" +//usage:#define start_stop_daemon_full_usage "\n\n" +//usage: "Search for matching processes, and then\n" +//usage: "-K: stop all matching processes\n" +//usage: "-S: start a process unless a matching process is found\n" +//usage: "\nProcess matching:" +//usage: "\n -u USERNAME|UID Match only this user's processes" +//usage: "\n -n NAME Match processes with NAME" +//usage: "\n in comm field in /proc/PID/stat" +//usage: "\n -x EXECUTABLE Match processes with this command" +//usage: "\n command in /proc/PID/cmdline" +//usage: "\n -p FILE Match a process with PID from FILE" +//usage: "\n All specified conditions must match" +//usage: "\n-S only:" +//usage: "\n -x EXECUTABLE Program to run" +//usage: "\n -a NAME Zeroth argument" +//usage: "\n -b Background" +//usage: IF_FEATURE_START_STOP_DAEMON_FANCY( +//usage: "\n -N N Change nice level" +//usage: ) +//usage: "\n -c USER[:[GRP]] Change user/group" +//usage: "\n -m Write PID to pidfile specified by -p" +//usage: "\n-K only:" +//usage: "\n -s SIG Signal to send" +//usage: "\n -t Match only, exit with 0 if found" +//usage: "\nOther:" +//usage: IF_FEATURE_START_STOP_DAEMON_FANCY( +//usage: "\n -o Exit with status 0 if nothing is done" +//usage: "\n -v Verbose" +//usage: ) +//usage: "\n -q Quiet" + +/* Override ENABLE_FEATURE_PIDFILE */ +#define WANT_PIDFILE 1 +#include "libbb.h" +#include "common_bufsiz.h" + +struct pid_list { + struct pid_list *next; + pid_t pid; +}; + +enum { + CTX_STOP = (1 << 0), + CTX_START = (1 << 1), + OPT_BACKGROUND = (1 << 2), // -b + OPT_QUIET = (1 << 3), // -q + OPT_TEST = (1 << 4), // -t + OPT_MAKEPID = (1 << 5), // -m + OPT_a = (1 << 6), // -a + OPT_n = (1 << 7), // -n + OPT_s = (1 << 8), // -s + OPT_u = (1 << 9), // -u + OPT_c = (1 << 10), // -c + OPT_x = (1 << 11), // -x + OPT_p = (1 << 12), // -p + OPT_OKNODO = (1 << 13) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -o + OPT_VERBOSE = (1 << 14) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -v + OPT_NICELEVEL = (1 << 15) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -N +}; +#define QUIET (option_mask32 & OPT_QUIET) +#define TEST (option_mask32 & OPT_TEST) + +struct globals { + struct pid_list *found_procs; + char *userspec; + char *cmdname; + char *execname; + char *pidfile; + char *execname_cmpbuf; + unsigned execname_sizeof; + int user_id; + smallint signal_nr; +#ifdef OLDER_VERSION_OF_X + struct stat execstat; +#endif +} FIX_ALIASING; +#define G (*(struct globals*)bb_common_bufsiz1) +#define userspec (G.userspec ) +#define cmdname (G.cmdname ) +#define execname (G.execname ) +#define pidfile (G.pidfile ) +#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) + +#ifdef OLDER_VERSION_OF_X +/* -x,--exec EXECUTABLE + * Look for processes with matching /proc/$PID/exe. + * Match is performed using device+inode. + */ +static int pid_is_exec(pid_t pid) +{ + struct stat st; + char buf[sizeof("/proc/%u/exe") + sizeof(int)*3]; + + sprintf(buf, "/proc/%u/exe", (unsigned)pid); + if (stat(buf, &st) < 0) + return 0; + if (st.st_dev == G.execstat.st_dev + && st.st_ino == G.execstat.st_ino) + return 1; + return 0; +} +#else +static int pid_is_exec(pid_t pid) +{ + ssize_t bytes; + char buf[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; + char *procname, *exelink; + int match; + + procname = buf + sprintf(buf, "/proc/%u/exe", (unsigned)pid) - 3; + + exelink = xmalloc_readlink(buf); + match = (exelink && strcmp(execname, exelink) == 0); + free(exelink); + if (match) + return match; + + strcpy(procname, "cmdline"); + bytes = open_read_close(buf, G.execname_cmpbuf, G.execname_sizeof); + if (bytes > 0) { + G.execname_cmpbuf[bytes] = '\0'; + return strcmp(execname, G.execname_cmpbuf) == 0; + } + return 0; +} +#endif + +static int pid_is_name(pid_t pid) +{ + /* /proc/PID/stat is "PID (comm_15_bytes_max) ..." */ + char buf[32]; /* should be enough */ + char *p, *pe; + + sprintf(buf, "/proc/%u/stat", (unsigned)pid); + if (open_read_close(buf, buf, sizeof(buf) - 1) < 0) + return 0; + buf[sizeof(buf) - 1] = '\0'; /* paranoia */ + p = strchr(buf, '('); + if (!p) + return 0; + pe = strrchr(++p, ')'); + if (!pe) + return 0; + *pe = '\0'; + /* we require comm to match and to not be truncated */ + /* in Linux, if comm is 15 chars, it may be a truncated + * name, so we don't allow that to match */ + if (strlen(p) >= COMM_LEN - 1) /* COMM_LEN is 16 */ + return 0; + return strcmp(p, cmdname) == 0; +} + +static int pid_is_user(int pid) +{ + struct stat sb; + char buf[sizeof("/proc/") + sizeof(int)*3]; + + sprintf(buf, "/proc/%u", (unsigned)pid); + if (stat(buf, &sb) != 0) + return 0; + return (sb.st_uid == (uid_t)user_id); +} + +static void check(int pid) +{ + struct pid_list *p; + + if (execname && !pid_is_exec(pid)) { + return; + } + if (cmdname && !pid_is_name(pid)) { + return; + } + if (userspec && !pid_is_user(pid)) { + return; + } + p = xmalloc(sizeof(*p)); + p->next = G.found_procs; + p->pid = pid; + G.found_procs = p; +} + +static void do_pidfile(void) +{ + FILE *f; + unsigned pid; + + f = fopen_for_read(pidfile); + if (f) { + if (fscanf(f, "%u", &pid) == 1) + check(pid); + fclose(f); + } else if (errno != ENOENT) + bb_perror_msg_and_die("open pidfile %s", pidfile); +} + +static void do_procinit(void) +{ + DIR *procdir; + struct dirent *entry; + int pid; + + if (pidfile) { + do_pidfile(); + return; + } + + procdir = xopendir("/proc"); + + pid = 0; + while (1) { + errno = 0; /* clear any previous error */ + entry = readdir(procdir); +// TODO: this check is too generic, it's better +// to check for exact errno(s) which mean that we got stale entry + if (errno) /* Stale entry, process has died after opendir */ + continue; + if (!entry) /* EOF, no more entries */ + break; + pid = bb_strtou(entry->d_name, NULL, 10); + if (errno) /* NaN */ + continue; + check(pid); + } + closedir(procdir); + if (!pid) + bb_error_msg_and_die("nothing in /proc - not mounted?"); +} + +static int do_stop(void) +{ + char *what; + struct pid_list *p; + int killed = 0; + + if (cmdname) { + if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(cmdname); + if (!ENABLE_FEATURE_CLEAN_UP) what = cmdname; + } else if (execname) { + if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(execname); + if (!ENABLE_FEATURE_CLEAN_UP) what = execname; + } else if (pidfile) { + what = xasprintf("process in pidfile '%s'", pidfile); + } else if (userspec) { + what = xasprintf("process(es) owned by '%s'", userspec); + } else { + bb_error_msg_and_die("internal error, please report"); + } + + if (!G.found_procs) { + if (!QUIET) + printf("no %s found; none killed\n", what); + killed = -1; + goto ret; + } + for (p = G.found_procs; p; p = p->next) { + if (kill(p->pid, TEST ? 0 : signal_nr) == 0) { + killed++; + } else { + bb_perror_msg("warning: killing process %u", (unsigned)p->pid); + p->pid = 0; + if (TEST) { + /* Example: -K --test --pidfile PIDFILE detected + * that PIDFILE's pid doesn't exist */ + killed = -1; + goto ret; + } + } + } + if (!QUIET && killed) { + printf("stopped %s (pid", what); + for (p = G.found_procs; p; p = p->next) + if (p->pid) + printf(" %u", (unsigned)p->pid); + puts(")"); + } + ret: + if (ENABLE_FEATURE_CLEAN_UP) + free(what); + return killed; +} + +#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS +static const char start_stop_daemon_longopts[] ALIGN1 = + "stop\0" No_argument "K" + "start\0" No_argument "S" + "background\0" No_argument "b" + "quiet\0" No_argument "q" + "test\0" No_argument "t" + "make-pidfile\0" No_argument "m" +# if ENABLE_FEATURE_START_STOP_DAEMON_FANCY + "oknodo\0" No_argument "o" + "verbose\0" No_argument "v" + "nicelevel\0" Required_argument "N" +# endif + "startas\0" Required_argument "a" + "name\0" Required_argument "n" + "signal\0" Required_argument "s" + "user\0" Required_argument "u" + "chuid\0" Required_argument "c" + "exec\0" Required_argument "x" + "pidfile\0" Required_argument "p" +# if ENABLE_FEATURE_START_STOP_DAEMON_FANCY + "retry\0" Required_argument "R" +# endif + ; +# define GETOPT32 getopt32long +# define LONGOPTS start_stop_daemon_longopts, +#else +# define GETOPT32 getopt32 +# define LONGOPTS +#endif + +int start_stop_daemon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opt; + char *signame; + char *startas; + char *chuid; +#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY +// char *retry_arg = NULL; +// int retries = -1; + char *opt_N; +#endif + + INIT_G(); + + opt = GETOPT32(argv, "^" + "KSbqtma:n:s:u:c:x:p:" + IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:") + /* -K or -S is required; they are mutually exclusive */ + /* -p is required if -m is given */ + /* -xpun (at least one) is required if -K is given */ + /* -xa (at least one) is required if -S is given */ + /* -q turns off -v */ + "\0" + "K:S:K--S:S--K:m?p:K?xpun:S?xa" + IF_FEATURE_START_STOP_DAEMON_FANCY("q-v"), + LONGOPTS + &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile + IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N) + /* We accept and ignore -R / --retry */ + IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL) + ); + + if (opt & OPT_s) { + signal_nr = get_signum(signame); + if (signal_nr < 0) bb_show_usage(); + } + + if (!(opt & OPT_a)) + startas = execname; + if (!execname) /* in case -a is given and -x is not */ + execname = startas; + if (execname) { + G.execname_sizeof = strlen(execname) + 1; + G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1); + } + +// IF_FEATURE_START_STOP_DAEMON_FANCY( +// if (retry_arg) +// retries = xatoi_positive(retry_arg); +// ) + //argc -= optind; + argv += optind; + + if (userspec) { + user_id = bb_strtou(userspec, NULL, 10); + if (errno) + user_id = xuname2uid(userspec); + } + /* Both start and stop need to know current processes */ + do_procinit(); + + if (opt & CTX_STOP) { + int i = do_stop(); + return (opt & OPT_OKNODO) ? 0 : (i <= 0); + } + + if (G.found_procs) { + if (!QUIET) + printf("%s is already running\n%u\n", execname, (unsigned)G.found_procs->pid); + return !(opt & OPT_OKNODO); + } + +#ifdef OLDER_VERSION_OF_X + if (execname) + xstat(execname, &G.execstat); +#endif + + *--argv = startas; + if (opt & OPT_BACKGROUND) { +#if BB_MMU + bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS + DAEMON_DOUBLE_FORK); + /* DAEMON_DEVNULL_STDIO is superfluous - + * it's always done by bb_daemonize() */ +#else + /* Daemons usually call bb_daemonize_or_rexec(), but SSD can do + * without: SSD is not itself a daemon, it _execs_ a daemon. + * The usual NOMMU problem of "child can't run indefinitely, + * it must exec" does not bite us: we exec anyway. + */ + pid_t pid = xvfork(); + if (pid != 0) { + /* parent */ + /* why _exit? the child may have changed the stack, + * so "return 0" may do bad things */ + _exit(EXIT_SUCCESS); + } + /* Child */ + setsid(); /* detach from controlling tty */ + /* Redirect stdio to /dev/null, close extra FDs */ + bb_daemon_helper(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS); +#endif + } + if (opt & OPT_MAKEPID) { + /* User wants _us_ to make the pidfile */ + write_pidfile(pidfile); + } + if (opt & OPT_c) { + struct bb_uidgid_t ugid; + parse_chown_usergroup_or_die(&ugid, chuid); + if (ugid.uid != (uid_t) -1L) { + struct passwd *pw = xgetpwuid(ugid.uid); + if (ugid.gid != (gid_t) -1L) + pw->pw_gid = ugid.gid; + /* initgroups, setgid, setuid: */ + change_identity(pw); + } else if (ugid.gid != (gid_t) -1L) { + xsetgid(ugid.gid); + setgroups(1, &ugid.gid); + } + } +#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY + if (opt & OPT_NICELEVEL) { + /* Set process priority */ + int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2); + if (setpriority(PRIO_PROCESS, 0, prio) < 0) { + bb_perror_msg_and_die("setpriority(%d)", prio); + } + } +#endif + execvp(startas, argv); + bb_perror_msg_and_die("can't execute '%s'", startas); +} diff --git a/busybox/debianutils/which.c b/busybox/debianutils/which.c new file mode 100644 index 00000000000000..02f77a216ae675 --- /dev/null +++ b/busybox/debianutils/which.c @@ -0,0 +1,73 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 1999-2004 by Erik Andersen + * Copyright (C) 2006 Gabriel Somlo + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config WHICH +//config: bool "which (3.7 kb)" +//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_NOFORK(which, which, BB_DIR_USR_BIN, BB_SUID_DROP, which)) + +//kbuild:lib-$(CONFIG_WHICH) += which.o + +//usage:#define which_trivial_usage +//usage: "[COMMAND]..." +//usage:#define which_full_usage "\n\n" +//usage: "Locate a COMMAND" +//usage: +//usage:#define which_example_usage +//usage: "$ which login\n" +//usage: "/bin/login\n" + +#include "libbb.h" + +int which_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int which_main(int argc UNUSED_PARAM, char **argv) +{ + char *env_path; + int status = 0; + /* This sizeof(): bb_default_root_path is shorter than BB_PATH_ROOT_PATH */ + char buf[sizeof(BB_PATH_ROOT_PATH)]; + + env_path = getenv("PATH"); + if (!env_path) + /* env_path must be writable, and must not alloc, so... */ + env_path = strcpy(buf, bb_default_root_path); + + getopt32(argv, "^" "a" "\0" "-1"/*at least one arg*/); + argv += optind; + + do { + int missing = 1; + + /* If file contains a slash don't use PATH */ + if (strchr(*argv, '/')) { + if (file_is_executable(*argv)) { + missing = 0; + puts(*argv); + } + } else { + char *path; + char *p; + + path = env_path; + /* NOFORK NB: xmalloc inside find_executable(), must have no allocs above! */ + while ((p = find_executable(*argv, &path)) != NULL) { + missing = 0; + puts(p); + free(p); + if (!option_mask32) /* -a not set */ + break; + } + } + status |= missing; + } while (*++argv); + + return status; +} diff --git a/busybox/docs/.gitignore b/busybox/docs/.gitignore new file mode 100644 index 00000000000000..3d1c5bd8ad4519 --- /dev/null +++ b/busybox/docs/.gitignore @@ -0,0 +1,5 @@ +/busybox.1 +/BusyBox.html +/busybox.net +/BusyBox.txt +/busybox.pod diff --git a/busybox/docs/Kconfig-language.txt b/busybox/docs/Kconfig-language.txt new file mode 100644 index 00000000000000..0ba8932ba8b6d0 --- /dev/null +++ b/busybox/docs/Kconfig-language.txt @@ -0,0 +1,255 @@ +Introduction +------------ + +The configuration database is collection of configuration options +organized in a tree structure: + + +- Code maturity level options + | +- Prompt for development and/or incomplete code/drivers + +- General setup + | +- Networking support + | +- System V IPC + | +- BSD Process Accounting + | +- Sysctl support + +- Loadable module support + | +- Enable loadable module support + | +- Set version information on all module symbols + | +- Kernel module loader + +- ... + +Every entry has its own dependencies. These dependencies are used +to determine the visible of an entry. Any child entry is only +visible if its parent entry is also visible. + +Menu entries +------------ + +Most entries define a config option, all other entries help to organize +them. A single configuration option is defined like this: + +config MODVERSIONS + bool "Set version information on all module symbols" + depends MODULES + help + Usually, modules have to be recompiled whenever you switch to a new + kernel. ... + +Every line starts with a key word and can be followed by multiple +arguments. "config" starts a new config entry. The following lines +define attributes for this config option. Attributes can be the type of +the config option, input prompt, dependencies, help text and default +values. A config option can be defined multiple times with the same +name, but every definition can have only a single input prompt and the +type must not conflict. + +Menu attributes +--------------- + +A menu entry can have a number of attributes. Not all of them are +applicable everywhere (see syntax). + +- type definition: "bool"/"tristate"/"string"/"hex"/"integer" + Every config option must have a type. There are only two basic types: + tristate and string, the other types base on these two. The type + definition optionally accepts an input prompt, so these two examples + are equivalent: + + bool "Networking support" + and + bool + prompt "Networking support" + +- input prompt: "prompt" ["if" ] + Every menu entry can have at most one prompt, which is used to display + to the user. Optionally dependencies only for this prompt can be added + with "if". + +- default value: "default" ["if" ] + A config option can have any number of default values. If multiple + default values are visible, only the first defined one is active. + Default values are not limited to the menu entry, where they are + defined, this means the default can be defined somewhere else or be + overridden by an earlier definition. + The default value is only assigned to the config symbol if no other + value was set by the user (via the input prompt above). If an input + prompt is visible the default value is presented to the user and can + be overridden by him. + Optionally dependencies only for this default value can be added with + "if". + +- dependencies: "depends on"/"requires" + This defines a dependency for this menu entry. If multiple + dependencies are defined they are connected with '&&'. Dependencies + are applied to all other options within this menu entry (which also + accept "if" expression), so these two examples are equivalent: + + bool "foo" if BAR + default y if BAR + and + depends on BAR + bool "foo" + default y + +- help text: "help" + This defines a help text. The end of the help text is determined by + the level indentation, this means it ends at the first line which has + a smaller indentation than the first line of the help text. + + +Menu dependencies +----------------- + +Dependencies define the visibility of a menu entry and can also reduce +the input range of tristate symbols. The tristate logic used in the +expressions uses one more state than normal boolean logic to express the +module state. Dependency expressions have the following syntax: + + ::= (1) + '=' (2) + '!=' (3) + '(' ')' (4) + '!' (5) + '||' (6) + '&&' (7) + +Expressions are listed in decreasing order of precedence. + +(1) Convert the symbol into an expression. Boolean and tristate symbols + are simply converted into the respective expression values. All + other symbol types result in 'n'. +(2) If the values of both symbols are equal, it returns 'y', + otherwise 'n'. +(3) If the values of both symbols are equal, it returns 'n', + otherwise 'y'. +(4) Returns the value of the expression. Used to override precedence. +(5) Returns the result of (2-/expr/). +(6) Returns the result of min(/expr/, /expr/). +(7) Returns the result of max(/expr/, /expr/). + +An expression can have a value of 'n', 'm' or 'y' (or 0, 1, 2 +respectively for calculations). A menu entry becomes visible when it's +expression evaluates to 'm' or 'y'. + +There are two type of symbols: constant and nonconstant symbols. +Nonconstant symbols are the most common ones and are defined with the +'config' statement. Nonconstant symbols consist entirely of alphanumeric +characters or underscores. +Constant symbols are only part of expressions. Constant symbols are +always surrounded by single or double quotes. Within the quote any +other character is allowed and the quotes can be escaped using '\'. + +Menu structure +-------------- + +The position of a menu entry in the tree is determined in two ways. First +it can be specified explicitely: + +menu "Network device support" + depends NET + +config NETDEVICES + ... + +endmenu + +All entries within the "menu" ... "endmenu" block become a submenu of +"Network device support". All subentries inherit the dependencies from +the menu entry, e.g. this means the dependency "NET" is added to the +dependency list of the config option NETDEVICES. + +The other way to generate the menu structure is done by analyzing the +dependencies. If a menu entry somehow depends on the previous entry, it +can be made a submenu of it. First the the previous (parent) symbol must +be part of the dependency list and then one of these two condititions +must be true: +- the child entry must become invisible, if the parent is set to 'n' +- the child entry must only be visible, if the parent is visible + +config MODULES + bool "Enable loadable module support" + +config MODVERSIONS + bool "Set version information on all module symbols" + depends MODULES + +comment "module support disabled" + depends !MODULES + +MODVERSIONS directly depends on MODULES, this means it's only visible if +MODULES is different from 'n'. The comment on the other hand is always +visible when MODULES it's visible (the (empty) dependency of MODULES is +also part of the comment dependencies). + + +Kconfig syntax +-------------- + +The configuration file describes a series of menu entries, where every +line starts with a keyword (except help texts). The following keywords +end a menu entry: +- config +- choice/endchoice +- comment +- menu/endmenu +- if/endif +- source +The first four also start the definition of a menu entry. + +config: + + "config" + + +This defines a config symbol and accepts any of above +attributes as options. + +choices: + + "choice" + + + "endchoice" + +This defines a choice group and accepts any of above attributes as +options. A choice can only be of type bool or tristate, while a boolean +choice only allows a single config entry to be selected, a tristate +choice also allows any number of config entries to be set to 'm'. This +can be used if multiple drivers for a single hardware exists and only a +single driver can be compiled/loaded into the kernel, but all drivers +can be compiled as modules. +A choice accepts another option "optional", which allows to set the +choice to 'n' and no entry needs to be selected. + +comment: + + "comment" + + +This defines a comment which is displayed to the user during the +configuration process and is also echoed to the output files. The only +possible options are dependencies. + +menu: + + "menu" + + + "endmenu" + +This defines a menu block, see "Menu structure" above for more +information. The only possible options are dependencies. + +if: + + "if" + + "endif" + +This defines an if block. The dependency expression is appended +to all enclosed menu entries. + +source: + + "source" + +This reads the specified configuration file. This file is always parsed. diff --git a/busybox/docs/Serial-Programming-HOWTO.txt b/busybox/docs/Serial-Programming-HOWTO.txt new file mode 100644 index 00000000000000..8a3954b0906c6b --- /dev/null +++ b/busybox/docs/Serial-Programming-HOWTO.txt @@ -0,0 +1,424 @@ +Downloaded from http://www.lafn.org/~dave/linux/Serial-Programming-HOWTO.txt +Seems to be somewhat old, but contains useful bits for getty.c hacking +============================================================================ + + The Linux Serial Programming HOWTO, Part 1 of 2 + By Vernon C. Hoxie + v2.0 10 September 1999 + + This document describes how to program communications with devices + over a serial port on a Linux box. + ______________________________________________________________________ + + Table of Contents + + 1. Copyright + + 2. Introduction + + 3. Opening + + 4. Commands + + 5. Changing Baud Rates + + 6. Additional Control Calls + + 6.1 Sending a "break". + 6.2 Hardware flow control. + 6.3 Flushing I/O buffers. + + 7. Modem control + + 8. Process Groups + + 8.1 Sessions + 8.2 Process Groups + 8.3 Controlling Terminal + 8.3.1 Get the foreground group process id. + 8.3.2 Set the foreground process group id of a terminal. + 8.3.3 Get process group id. + + 9. Lockfiles + + 10. Additional Information + + 11. Feedback + + ______________________________________________________________________ + + 1. Copyright + + The Linux Serial-Programming-HOWTO is copyright (C) 1997 by Vernon + Hoxie. Linux HOWTO documents may be reproduced and distributed in + whole or in part, in any medium physical or electronic, as long as + this copyright notice is retained on all copies. Commercial + redistribution is allowed and encouraged; however, the author would + like to be notified of any such distributions. + + All translations, derivative works, or aggregate works incorporating + this Linux HOWTO document must be covered under this copyright notice. + That is, you may not produce a derivative work from this HOWTO and + impose additional restrictions on its distribution. + + This version is a complete rewrite of the previous Serial-Programming- + HOWTO by Peter H. Baumann, + + 2. Introduction + + This HOWTO will attempt to give hints about how to write a program + which needs to access a serial port. Its principal focus will be on + the Linux implementation and what the meaning of the various library + functions available. + + Someone asked about which of several sequences of operations was + right. There is no absolute right way to accomplish an outcome. The + options available are too numerous. If your sequences produces the + desired results, then that is the right way for you. Another + programmer may select another set of options and get the same results. + His method is right for him. + + Neither of these methods may operate properly with some other + implementation of UNIX. It is strange that many of the concepts which + were implemented in the SYSV version have been dumped. Because UNIX + was developed by AT&T and much code has been generated on those + concepts, the AT&T version should be the standard to which others + should emulate. + + Now the standard is POSIX. + + It was once stated that the popularity of UNIX and C was that they + were created by programmers for programmers. Not by scholars who + insist on purity of style in deference to results and simplicity of + use. Not by committees with people who have diverse personal or + proprietary agenda. Now ANSI and POSIX have strayed from those + original clear and simply concepts. + + 3. Opening + + The various serial devices are opened just as any other file. + Although, the fopen(3) command may be used, the plain open(2) is + preferred. This call returns the file descriptor which is required + for the various commands that configure the interface. + + Open(2) has the format: + + #include + int open(char *path, int flags, [int mode]); + + In addition to the obvious O_RDWR, O_WRONLY and O_RDONLY, two + additional flags are available. These are O_NONBLOCK and O_NOCTTY. + Other flags listed in the open(2) manual page are not applicable to + serial devices. + + Normally, a serial device opens in "blocking" mode. This means that + the open() will not return until the Carrier Detect line from the port + is active, e.g. modem, is active. When opened with the O_NONBLOCK + flag set, the open() will return immediately regardless of the status + of the DCD line. The "blocking" mode also affects the read() call. + + The fcntl(2) command can be used to change the O_NONBLOCK flag anytime + after the device has been opened. + + The device driver and the data passing through it are controlled + according to settings in the struct termios. This structure is + defined in "/usr/include/termios.h". In the Linux tree, further + reference is made to "/usr/include/asm/termbits.h". + In blocking mode, a read(2) will block until data is available or a + signal is received. It is still subject to state of the ICANON flag. + + When the termios.c_lflag ICANON bit is set, input data is collected + into strings until a NL, EOF or EOL character is received. You can + define these in the termios.c_cc[] array. Also, ERASE and KILL + characters will operate on the incoming data before it is delivered to + the user. + + In non-canonical mode, incoming data is quantified by use of the + c_cc[VMIN and c_cc[VTIME] values in termios.c_cc[]. + + Some programmers use the select() call to detect the completion of a + read(). This is not the best way of checking for incoming data. + Select() is part of the SOCKETS scheme and too complex for most + applications. + + A full explanation of the fields of the termios structure is contained + in termios(7) of the Users Manual. A version is included in Part 2 of + this HOWTO document. + + 4. Commands + + Changes to the struct termios are made by retrieving the current + settings, making the desired changes and transmitting the modified + structure back to the kernel. + + The historic means of communicating with the kernel was by use of the + ioctl(fd, COMMAND, arg) system call. Then the purists in the + computer industry decided that this was not genetically consistent. + Their argument was that the argument changed its stripes. Sometimes + it was an int, sometimes it was a pointer to int and other times it + was a pointer to struct termios. Then there were those times it was + empty or NULL. These variations are dependent upon the COMMAND. + + As a alternative, the tc* series of functions were concocted. + + These are: + + int tcgetattr(int filedes, struct termios *termios_p); + int tcsetattr(int filedes, int optional_actions, + const struct termios *termios_p); + + instead of: + + int ioctl(int filedes, int command, + struct termios *termios_p); + + where command is TCGETS or one of TCSETS, TCSETSW or TCSETSF. + + The TCSETS command is comparable to the TCSANOW optional_action for + the tc* version. These direct the kernel to adopt the changes + immediately. Other pairs are: + + command optional_action Meaning + TCSETSW TCSADRAIN Change after all output has drained. + TCSETSF TCSAFLUSH Change after all output has drained + then discard any input characters + not read. + + Since the return code from either the ioctl(2) or the tcsetattr(2) + commands only indicate that the command was processed by the kernel. + These do not indicate whether or not the changes were actually + accomplished. Either of these commands should be followed by a call + to: + + ioctl(fd, TCGETS, &new_termios); + + or: + + tcgetattr(fd, &new_termios); + + A user function which makes changes to the termios structure should + define two struct termios variables. One of these variables should + contain the desired configuration. The other should contain a copy of + the kernels version. Then after the desired configuration has been + sent to the kernel, another call should be made to retrieve the + kernels version. Then the two compared. + + Here is an example of how to add RTS/CTS flow control: + + struct termios my_termios; + struct termios new_termios; + + tcgetattr(fd, &my_termios); + my_termios.c_flag |= CRTSCTS; + tcsetattr(fd, TCSANOW, &my_termios); + tcgetattr(fd, &new_termios); + if (memcmp(my_termios, new_termios, + sizeof(my_termios)) != 0) { + /* do some error handling */ + } + + 5. Changing Baud Rates + + With Linux, the baud rate can be changed using a technique similar to + add/delete RTS/CTS. + + struct termios my_termios; + struct termios new_termios; + + tcgetattr(fd, &my_termios); + my_termios.c_flag &= ~CBAUD; + my_termios.c_flag |= B19200; + tcsetattr(fd, TCSANOW, &my_termios); + tcgetattr(fd, &new_termios); + if (memcmp(my_termios, new_termios, + sizeof(my_termios)) != 0) { + /* do some error handling */ + } + + POSIX adds another method. They define: + + speed_t cfgetispeed(const struct termios *termios_p); + speed_t cfgetospeed(const struct termios *termios_p); + + library calls to extract the current input or output speed from the + struct termios pointed to with *termio_p. This is a variable defined + in the calling process. In practice, the data contained in this + termios, should be obtained by the tcgetattr() call or an ioctl() call + using the TCGETS command. + + The companion library calls are: + + int cfsetispeed(struct termios *termios_p, speed_t speed); + int cfsetospeed(struct termios *termios_p, speed_t speed); + + which are used to change the value of the baud rate in the locally + defined *termios_p. Following either of these calls, either a call to + tcsetattr() or ioctl() with one of TCSETS, TCSETSW or TCSETSF as the + command to transmit the change to the kernel. + + The cf* commands are preferred for portability. Some weird Unices use + a considerably different format of termios. + + Most implementations of Linux use only the input speed for both input + and output. These functions are defined in the application program by + reference to . In reality, they are in + /usr/include/asm/termbits.h. + + 6. Additional Control Calls + + 6.1. Sending a "break". + + int ioctl(fd, TCSBRK, int arg); + int tcsendbreak(fd, int arg); + + Send a break: Here the action differs between the conventional + ioctl() call and the POSIX call. For the conventional call, an arg of + '0' sets the break control line of the UART for 0.25 seconds. For the + POSIX command, the break line is set for arg times 0.1 seconds. + + 6.2. Hardware flow control. + + int ioctl(fd, TCXONC, int action); + int tcflow(fd, int action); + + The action flags are: + + o TCOOFF 0 suspend output + + o TCOON 1 restart output + + o TCIOFF 2 transmit STOP character to suspend input + + o TCION 3 transmit START character to restart input + + 6.3. Flushing I/O buffers. + + int ioctl(fd, TCFLSH, queue_selector); + int tcflush(fd, queue_selector); + + The queue_selector flags are: + + o TCIFLUSH 0 flush any data not yet read from the input buffer + + o TCOFLUSH 1 flush any data written to the output buffer but not + yet transmitted + + o TCIOFLUSH 2 flush both buffers + + 7. Modem control + + The hardware modem control lines can be monitored or modified by the + ioctl(2) system call. A set of comparable tc* calls apparently do not + exist. The form of this call is: + + int ioctl(fd, COMMAND, (int *)flags); + + The COMMANDS and their action are: + + o TIOCMBIS turn on control lines depending upon which bits are set + in flags. + + o TIOCMBIC turn off control lines depending upon which bits are + unset in flags. + o TIOCMGET the appropriate bits are set in flags according to the + current status + + o TIOCMSET the state of the UART is changed according to which bits + are set/unset in 'flags' + + The bit pattern of flags refer to the following control lines: + + o TIOCM_LE Line enable + + o TIOCM_DTR Data Terminal Ready + + o TIOCM_RTS Request to send + + o TIOCM_ST Secondary transmit + + o TIOCM_SR Secondary receive + + o TIOCM_CTS Clear to send + + o TIOCM_CAR Carrier detect + + o TIOCM_RNG Ring + + o TIOCM_DSR Data set ready + + It should be noted that some of these bits are controlled by the modem + and the UART cannot change them but their status can be sensed by + TIOCMGET. Also, most Personal Computers do not provide hardware for + secondary transmit and receive. + + There are also a pair of ioctl() to monitor these lines. They are + undocumented as far as I have learned. The commands are TIOCMIWAIT + and TCIOGICOUNT. They also differ between versions of the Linux + kernel. + + See the lines.c file in my "serial_suite" for an example of how these + can be used see + + 8. Process Groups + + 8.1. Sessions + + 8.2. Process Groups + + Any newly created process inherits the Process Group of its creator. + The Process Group leader has the same PID as PGID. + + 8.3. Controlling Terminal + + There are a series of ioctl(2) and tc*(2) calls which can be used to + monitor or to change the process group to which the device is + attached. + + 8.3.1. Get the foreground group process id. + + If there is no foreground group, a number not representing an existing + process group is returned. On error, a -1 is returned and errno is + set. + + int ioctl(fd, TIOCGPGRP, (pid_t *)pid); + int tcgetpgrp(fd, (pid_t *)pid); + + 8.3.2. Set the foreground process group id of a terminal. + + The fd must be the controlling terminal and be associated with the + session of the calling process. + + int ioctl(fd, TIOCSPGRP, (pid_t *)pid); + int tcsetpgrp(fd, (pid_t *)pid); + + 8.3.3. Get process group id. + + int ioctl(fd, TIOCGPGRP, &(pid_t)pid); + int tcgetpgrp(fd, &(pid_t)pid); + + 9. Lockfiles + + Any process which accesses a serial device should first check for the + existence of lock file for the desired device. If such a lock lock + file exists, this means that the device may be in use by another + process. + + Check my "libdevlocks-x.x.tgz" at + for an example of how these lock + files should be utilized. + + 10. Additional Information + + Check out my "serial_suite.tgz" for more information about programming + the serial ports at . There some + examples and some blurbs about setting up modems and comments about + some general considerations. + + 11. Feedback + + Please send me any corrections, questions, comments, suggestions, or + additional material. I would like to improve this HOWTO! Tell me + exactly what you don't understand, or what could be clearer. You can + reach me at via email. Please + include the version number of the Serial-Programming-HOWTO when + writing. diff --git a/busybox/docs/busybox_footer.pod b/busybox/docs/busybox_footer.pod new file mode 100644 index 00000000000000..92748eb72d67af --- /dev/null +++ b/busybox/docs/busybox_footer.pod @@ -0,0 +1,283 @@ +=head1 LIBC NSS + +GNU Libc (glibc) uses the Name Service Switch (NSS) to configure the behavior +of the C library for the local environment, and to configure how it reads +system data, such as passwords and group information. This is implemented +using an /etc/nsswitch.conf configuration file, and using one or more of the +/lib/libnss_* libraries. BusyBox tries to avoid using any libc calls that make +use of NSS. Some applets however, such as login and su, will use libc functions +that require NSS. + +If you enable CONFIG_USE_BB_PWD_GRP, BusyBox will use internal functions to +directly access the /etc/passwd, /etc/group, and /etc/shadow files without +using NSS. This may allow you to run your system without the need for +installing any of the NSS configuration files and libraries. + +When used with glibc, the BusyBox 'networking' applets will similarly require +that you install at least some of the glibc NSS stuff (in particular, +/etc/nsswitch.conf, /lib/libnss_dns*, /lib/libnss_files*, and /lib/libresolv*). + +Shameless Plug: As an alternative, one could use a C library such as uClibc. In +addition to making your system significantly smaller, uClibc does not require the +use of any NSS support files or libraries. + +=head1 MAINTAINER + +Denis Vlasenko + +=head1 AUTHORS + +The following people have contributed code to BusyBox whether they know it or +not. If you have written code included in BusyBox, you should probably be +listed here so you can obtain your bit of eternal glory. If you should be +listed here, or the description of what you have done needs more detail, or is +incorrect, please send in an update. + + +=for html
+ +Emanuele Aina + run-parts + +=for html
+ +Erik Andersen + + Tons of new stuff, major rewrite of most of the + core apps, tons of new apps as noted in header files. + Lots of tedious effort writing these boring docs that + nobody is going to actually read. + +=for html
+ +Laurence Anderson + + rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm + +=for html
+ +Jeff Angielski + + ftpput, ftpget + +=for html
+ +Edward Betts + + expr, hostid, logname, whoami + +=for html
+ +John Beppu + + du, nslookup, sort + +=for html
+ +Brian Candler + + tiny-ls(ls) + +=for html
+ +Randolph Chung + + fbset, ping, hostname + +=for html
+ +Dave Cinege + + more(v2), makedevs, dutmp, modularization, auto links file, + various fixes, Linux Router Project maintenance + +=for html
+ +Jordan Crouse + + ipcalc + +=for html
+ +Magnus Damm + + tftp client insmod powerpc support + +=for html
+ +Larry Doolittle + + pristine source directory compilation, lots of patches and fixes. + +=for html
+ +Glenn Engel + + httpd + +=for html
+ +Gennady Feldman + + Sysklogd (single threaded syslogd, IPC Circular buffer support, + logread), various fixes. + +=for html
+ +Karl M. Hegbloom + + cp_mv.c, the test suite, various fixes to utility.c, &c. + +=for html
+ +Daniel Jacobowitz + + mktemp.c + +=for html
+ +Matt Kraai + + documentation, bugfixes, test suite + +=for html
+ +Stephan Linz + + ipcalc, Red Hat equivalence + +=for html
+ +John Lombardo + + tr + +=for html
+ +Glenn McGrath + + Common unarchiving code and unarchiving applets, ifupdown, ftpgetput, + nameif, sed, patch, fold, install, uudecode. + Various bugfixes, review and apply numerous patches. + +=for html
+ +Manuel Novoa III + + cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes, + mesg, vconfig, make_directory, parse_mode, dirname, mode_string, + get_last_path_component, simplify_path, and a number trivial libbb routines + + also bug fixes, partial rewrites, and size optimizations in + ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir, + mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable, + interface, dutmp, ifconfig, route + +=for html
+ +Vladimir Oleynik + + cmdedit; xargs(current), httpd(current); + ports: ash, crond, fdisk, inetd, stty, traceroute, top; + locale, various fixes + and irreconcilable critic of everything not perfect. + +=for html
+ +Bruce Perens + + Original author of BusyBox in 1995, 1996. Some of his code can + still be found hiding here and there... + +=for html
+ +Tim Riker + + bug fixes, member of fan club + +=for html
+ +Kent Robotti + + reset, tons and tons of bug reports and patches. + +=for html
+ +Chip Rosenthal , + + wget - Contributed by permission of Covad Communications + +=for html
+ +Pavel Roskin + + Lots of bugs fixes and patches. + +=for html
+ +Gyepi Sam + + Remote logging feature for syslogd + +=for html
+ +Linus Torvalds + + mkswap, fsck.minix, mkfs.minix + +=for html
+ +Mark Whitley + + grep, sed, cut, xargs(previous), + style-guide, new-applet-HOWTO, bug fixes, etc. + +=for html
+ +Charles P. Wright + + gzip, mini-netcat(nc) + +=for html
+ +Enrique Zanardi + + tarcat (since removed), loadkmap, various fixes, Debian maintenance + +=for html
+ +Tito Ragusa + + devfsd and size optimizations in strings, openvt and deallocvt. + +=for html
+ +Paul Fox + + vi editing mode for ash, various other patches/fixes + +=for html
+ +Roberto A. Foglietta + + port: dnsd + +=for html
+ +Bernhard Reutner-Fischer + + misc + +=for html
+ +Mike Frysinger + + initial e2fsprogs, printenv, setarch, sum, misc + +=for html
+ +Jie Zhang + + fixed two bugs in msh and hush (exitcode of killed processes) + +=cut diff --git a/busybox/docs/busybox_header.pod b/busybox/docs/busybox_header.pod new file mode 100644 index 00000000000000..85a173e82d2cac --- /dev/null +++ b/busybox/docs/busybox_header.pod @@ -0,0 +1,82 @@ +# vi: set sw=4 ts=4: + +=head1 NAME + +BusyBox - The Swiss Army Knife of Embedded Linux + +=head1 SYNTAX + + busybox [arguments...] # or + + [arguments...] # if symlinked + +=head1 DESCRIPTION + +BusyBox combines tiny versions of many common UNIX utilities into a single +small executable. It provides minimalist replacements for most of the utilities +you usually find in GNU coreutils, util-linux, etc. The utilities in BusyBox +generally have fewer options than their full-featured GNU cousins; however, the +options that are included provide the expected functionality and behave very +much like their GNU counterparts. + +BusyBox has been written with size-optimization and limited resources in mind. +It is also extremely modular so you can easily include or exclude commands (or +features) at compile time. This makes it easy to customize your embedded +systems. To create a working system, just add /dev, /etc, and a Linux kernel. +BusyBox provides a fairly complete POSIX environment for any small or embedded +system. + +BusyBox is extremely configurable. This allows you to include only the +components you need, thereby reducing binary size. Run 'make config' or 'make +menuconfig' to select the functionality that you wish to enable. Then run +'make' to compile BusyBox using your configuration. + +After the compile has finished, you should use 'make install' to install +BusyBox. This will install the 'bin/busybox' binary, in the target directory +specified by CONFIG_PREFIX. CONFIG_PREFIX can be set when configuring BusyBox, +or you can specify an alternative location at install time (i.e., with a +command line like 'make CONFIG_PREFIX=/tmp/foo install'). If you enabled +any applet installation scheme (either as symlinks or hardlinks), these will +also be installed in the location pointed to by CONFIG_PREFIX. + +=head1 USAGE + +BusyBox is a multi-call binary. A multi-call binary is an executable program +that performs the same job as more than one utility program. That means there +is just a single BusyBox binary, but that single binary acts like a large +number of utilities. This allows BusyBox to be smaller since all the built-in +utility programs (we call them applets) can share code for many common +operations. + +You can also invoke BusyBox by issuing a command as an argument on the +command line. For example, entering + + /bin/busybox ls + +will also cause BusyBox to behave as 'ls'. + +Of course, adding '/bin/busybox' into every command would be painful. So most +people will invoke BusyBox using links to the BusyBox binary. + +For example, entering + + ln -s /bin/busybox ls + ./ls + +will cause BusyBox to behave as 'ls' (if the 'ls' command has been compiled +into BusyBox). Generally speaking, you should never need to make all these +links yourself, as the BusyBox build system will do this for you when you run +the 'make install' command. + +If you invoke BusyBox with no arguments, it will provide you with a list of the +applets that have been compiled into your BusyBox binary. + +=head1 COMMON OPTIONS + +Most BusyBox applets support the B<--help> argument to provide a terse runtime +description of their behavior. If the CONFIG_FEATURE_VERBOSE_USAGE option has +been enabled, more detailed usage information will also be available. + +=head1 COMMANDS + +Currently available applets include: diff --git a/busybox/docs/cgi/cl.html b/busybox/docs/cgi/cl.html new file mode 100644 index 00000000000000..4f8faae9fcd8b4 --- /dev/null +++ b/busybox/docs/cgi/cl.html @@ -0,0 +1,46 @@ +CGI Command line options

CGI Command line options

+

+ +

Specification

+ +The command line is only used in the case of an ISINDEX query. It is +not used in the case of an HTML form or any as yet undefined query +type. The server should search the query information (the QUERY_STRING environment variable) for a non-encoded += character to determine if the command line is to be used, if it +finds one, the command line is not to be used. This trusts the clients +to encode the = sign in ISINDEX queries, a practice which was +considered safe at the time of the design of this specification.

+ +For example, use the finger script and the ISINDEX interface to look up "httpd". You will see that the script will call itself with /cgi-bin/finger?httpd and will actually execute "finger httpd" on the command line and output the results to you. +

+If the server does find a "=" in the QUERY_STRING, +then the command line will not be used, and no decoding will be +performed. The query then remains intact for processing by an +appropriate FORM submission decoder. +Again, as an example, use this hyperlink to submit "httpd=name" to the finger script. Since this QUERY_STRING +contained an unencoded "=", nothing was decoded, the script didn't know +it was being submitted a valid query, and just gave you the default +finger form. +

+If the server finds that it cannot send the string due to internal +limitations (such as exec() or /bin/sh command line restrictions) the +server should include NO command line information and provide the +non-decoded query information in the environment +variable QUERY_STRING.

+


+

Examples

+ +Examples of the command line usage are much better demonstrated than explained. For these +examples, pay close attention to the script output which says what +argc and argv are.

+ +


+ +[Back]Return to the +interface specification

+ +CGI - Common Gateway Interface +

cgi@ncsa.uiuc.edu
+ + + diff --git a/busybox/docs/cgi/env.html b/busybox/docs/cgi/env.html new file mode 100644 index 00000000000000..66a548b4656bcc --- /dev/null +++ b/busybox/docs/cgi/env.html @@ -0,0 +1,149 @@ +CGI Environment Variables

CGI Environment Variables

+
+ +

+ +In order to pass data about the information request from the server to +the script, the server uses command line arguments as well as +environment variables. These environment variables are set when the +server executes the gateway program.

+ +


+

Specification

+ +

+The following environment variables are not request-specific and are +set for all requests:

+ +

    +
  • SERVER_SOFTWARE

    + + The name and version of the information server software answering + the request (and running the gateway). Format: name/version

    + +

  • SERVER_NAME

    + The server's hostname, DNS alias, or IP address as it would appear + in self-referencing URLs.

    + +

  • GATEWAY_INTERFACE

    + The revision of the CGI specification to which this server + complies. Format: CGI/revision

    + +

+ +
+ +The following environment variables are specific to the request being +fulfilled by the gateway program:

+ +

    +
  • SERVER_PROTOCOL

    + The name and revision of the information protocol this request came + in with. Format: protocol/revision

    + +

  • SERVER_PORT

    + The port number to which the request was sent.

    + +

  • REQUEST_METHOD

    + The method with which the request was made. For HTTP, this is + "GET", "HEAD", "POST", etc.

    + +

  • PATH_INFO

    + The extra path information, as given by the client. In other + words, scripts can be accessed by their virtual pathname, followed + by extra information at the end of this path. The extra + information is sent as PATH_INFO. This information should be + decoded by the server if it comes from a URL before it is passed + to the CGI script.

    + +

  • PATH_TRANSLATED

    + The server provides a translated version of PATH_INFO, which takes + the path and does any virtual-to-physical mapping to it.

    + +

  • SCRIPT_NAME

    + A virtual path to the script being executed, used for + self-referencing URLs.

    + +

  • QUERY_STRING

    + The information which follows the ? in the URL + which referenced this script. This is the query information. It + should not be decoded in any fashion. This variable should always + be set when there is query information, regardless of command line decoding.

    + +

  • REMOTE_HOST

    + The hostname making the request. If the server does not have this + information, it should set REMOTE_ADDR and leave this unset.

    + +

  • REMOTE_ADDR

    + The IP address of the remote host making the request.

    + +

  • AUTH_TYPE

    + If the server supports user authentication, and the script is + protects, this is the protocol-specific authentication method used + to validate the user.

    + +

  • REMOTE_USER

    + If the server supports user authentication, and the script is + protected, this is the username they have authenticated as.

    +

  • REMOTE_IDENT

    + If the HTTP server supports RFC 931 identification, then this + variable will be set to the remote user name retrieved from the + server. Usage of this variable should be limited to logging only. +

    + +

  • CONTENT_TYPE

    + For queries which have attached information, such as HTTP POST and + PUT, this is the content type of the data.

    + +

  • CONTENT_LENGTH

    + The length of the said content as given by the client.

    + +

+ + +
+ +In addition to these, the header lines received from the client, if +any, are placed into the environment with the prefix HTTP_ followed by +the header name. Any - characters in the header name are changed to _ +characters. The server may exclude any headers which it has already +processed, such as Authorization, Content-type, and Content-length. If +necessary, the server may choose to exclude any or all of these +headers if including them would exceed any system environment +limits.

+ +An example of this is the HTTP_ACCEPT variable which was defined in +CGI/1.0. Another example is the header User-Agent.

+ +

    +
  • HTTP_ACCEPT

    + The MIME types which the client will accept, as given by HTTP + headers. Other protocols may need to get this information from + elsewhere. Each item in this list should be separated by commas as + per the HTTP spec.

    + + Format: type/subtype, type/subtype

    + + +

  • HTTP_USER_AGENT

    + + The browser the client is using to send the request. General +format: software/version library/version.

    + +

+ +
+

Examples

+ +Examples of the setting of environment variables are really much better +demonstrated than explained.

+ +


+ +[Back]Return to the +interface specification

+ +CGI - Common Gateway Interface +

cgi@ncsa.uiuc.edu
+ + diff --git a/busybox/docs/cgi/in.html b/busybox/docs/cgi/in.html new file mode 100644 index 00000000000000..7ee5fe60195f89 --- /dev/null +++ b/busybox/docs/cgi/in.html @@ -0,0 +1,33 @@ +CGI Script input

CGI Script Input

+
+ +

Specification

+ +For requests which have information attached after the header, such as +HTTP POST or PUT, the information will be sent to the script on stdin. +

+ +The server will send CONTENT_LENGTH bytes on +this file descriptor. Remember that it will give the CONTENT_TYPE of the data as well. The server is +in no way obligated to send end-of-file after the script reads +CONTENT_LENGTH bytes.

+


+

Example

+ +Let's take a form with METHOD="POST" as an example. Let's say the form +results are 7 bytes encoded, and look like a=b&b=c. +

+ +In this case, the server will set CONTENT_LENGTH to 7 and CONTENT_TYPE +to application/x-www-form-urlencoded. The first byte on the script's +standard input will be "a", followed by the rest of the encoded string.

+ +


+ +[Back]Return to the +interface specification

+ +CGI - Common Gateway Interface +

cgi@ncsa.uiuc.edu
+ + diff --git a/busybox/docs/cgi/interface.html b/busybox/docs/cgi/interface.html new file mode 100644 index 00000000000000..0be016b4c4127e --- /dev/null +++ b/busybox/docs/cgi/interface.html @@ -0,0 +1,29 @@ +The Common Gateway Interface Specification +[http://hoohoo.ncsa.uiuc.edu/cgi/interface.html] +

The CGI Specification

+ +
+ +This is the specification for CGI version 1.1, or CGI/1.1. Further +revisions of this protocol are guaranteed to be backward compatible. +

+ +The server and the CGI script communicate in four major ways. Each of +the following is a hotlink to graphic detail.

+ +

+
+ + +[Back]Return to the overview

+ + + +CGI - Common Gateway Interface +

cgi@ncsa.uiuc.edu
+ diff --git a/busybox/docs/cgi/out.html b/busybox/docs/cgi/out.html new file mode 100644 index 00000000000000..5266985b5b058a --- /dev/null +++ b/busybox/docs/cgi/out.html @@ -0,0 +1,126 @@ +CGI Script output

CGI Script Output

+
+ +

Script output

+ +The script sends its output to stdout. This output can either be a +document generated by the script, or instructions to the server for +retrieving the desired output.

+


+ +

Script naming conventions

+ +Normally, scripts produce output which is interpreted and sent back to +the client. An advantage of this is that the scripts do not need to +send a full HTTP/1.0 header for every request.

+ +Some scripts may want to avoid the extra overhead of the server +parsing their output, and talk directly to the client. In order to +distinguish these scripts from the other scripts, CGI requires that +the script name begins with nph- if a script does not want the server +to parse its header. In this case, it is the script's responsibility +to return a valid HTTP/1.0 (or HTTP/0.9) response to the client.

+ +


+

Parsed headers

+ +The output of scripts begins with a small header. This header consists +of text lines, in the same format as an +HTTP header, terminated by a blank line (a line with only a +linefeed or CR/LF).

+ +Any headers which are not server directives are sent directly back to +the client. Currently, this specification defines three server +directives:

+ +

    +
  • Content-type

    + + This is the MIME type of the document you are returning.

    + +

  • Location

    + + This is used to specify to the server that you are returning a + reference to a document rather than an actual document.

    + + If the argument to this is a URL, the server will issue a redirect + to the client.

    + + If the argument to this is a virtual path, the server will + retrieve the document specified as if the client had requested + that document originally. ? directives will work in here, but # + directives must be redirected back to the client.

    + + +

  • Status

    + + This is used to give the server an HTTP/1.0 status +line to send to the client. The format is nnn xxxxx, +where nnn is the 3-digit status code, and +xxxxx is the reason string, such as "Forbidden".

    + +

+ +
+

Examples

+ +Let's say I have a fromgratz to HTML converter. When my converter is +finished with its work, it will output the following on stdout (note +that the lines beginning and ending with --- are just for illustration +and would not be output):

+ +

--- start of output ---
+Content-type: text/html
+
+--- end of output ---
+
+ +Note the blank line after Content-type.

+ +Now, let's say I have a script which, in certain instances, wants to +return the document /path/doc.txt from this server just +as if the user had actually requested +http://server:port/path/doc.txt to begin with. In this +case, the script would output:

+

--- start of output ---
+Location: /path/doc.txt
+
+--- end of output ---
+
+ +The server would then perform the request and send it to the client. +

+ +Let's say that I have a script which wants to reference our gopher +server. In this case, if the script wanted to refer the user to +gopher://gopher.ncsa.uiuc.edu/, it would output:

+ +

--- start of output ---
+Location: gopher://gopher.ncsa.uiuc.edu/
+
+--- end of output ---
+
+ +Finally, I have a script which wants to talk to the client directly. +In this case, if the script is referenced with SERVER_PROTOCOL of HTTP/1.0, +the script would output the following HTTP/1.0 response:

+ +

--- start of output ---
+HTTP/1.0 200 OK
+Server: NCSA/1.0a6
+Content-type: text/plain
+
+This is a plaintext document generated on the fly just for you.
+
+--- end of output ---
+
+ + +
+ +[Back]Return to the +interface specification

+ +CGI - Common Gateway Interface +

cgi@ncsa.uiuc.edu
+ diff --git a/busybox/docs/contributing.txt b/busybox/docs/contributing.txt new file mode 100644 index 00000000000000..e3289fd49b6e0b --- /dev/null +++ b/busybox/docs/contributing.txt @@ -0,0 +1,439 @@ +Contributing To Busybox +======================= + +This document describes what you need to do to contribute to Busybox, where +you can help, guidelines on testing, and how to submit a well-formed patch +that is more likely to be accepted. + +The Busybox home page is at: http://busybox.net/ + + + +Pre-Contribution Checklist +-------------------------- + +So you want to contribute to Busybox, eh? Great, wonderful, glad you want to +help. However, before you dive in, headlong and hotfoot, there are some things +you need to do: + + +Checkout the Latest Code +~~~~~~~~~~~~~~~~~~~~~~~~ + +This is a necessary first step. Please do not try to work with the last +released version, as there is a good chance that somebody has already fixed +the bug you found. Somebody might have even added the feature you had in mind. +Don't make your work obsolete before you start! + +For information on how to check out Busybox development tree, please look at the +following links: + + http://busybox.net/source.html + + +Read the Mailing List +~~~~~~~~~~~~~~~~~~~~~ + +No one is required to read the entire archives of the mailing list, but you +should at least read up on what people have been talking about lately. If +you've recently discovered a problem, chances are somebody else has too. If +you're the first to discover a problem, post a message and let the rest of us +know. + +Archives can be found here: + + http://busybox.net/lists/busybox/ + +If you have a serious interest in Busybox, i.e., you are using it day-to-day or +as part of an embedded project, it would be a good idea to join the mailing +list. + +A web-based sign-up form can be found here: + + http://busybox.net/mailman/listinfo/busybox + + +Coordinate with the Applet Maintainer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some (not all) of the applets in Busybox are "owned" by a maintainer who has +put significant effort into it and is probably more familiar with it than +others. To find the maintainer of an applet, look at the top of the .c file +for a name following the word 'Copyright' or 'Written by' or 'Maintainer'. + +Before plunging ahead, it's a good idea to send a message to the mailing list +that says: "Hey, I was thinking about adding the 'transmogrify' feature to the +'foo' applet. Would this be useful? Is anyone else working on it?" You might +want to CC the maintainer (if any) with your question. + + + +Areas Where You Can Help +------------------------ + +Busybox can always use improvement! If you're looking for ways to help, there +are a variety of areas where you could help. + + +What Busybox Doesn't Need +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before listing the areas where you _can_ help, it's worthwhile to mention the +areas where you shouldn't bother. While Busybox strives to be the "Swiss Army +Knife" of embedded Linux, there are some applets that will not be accepted: + + - Any filesystem manipulation tools: Busybox is filesystem independent and + we do not want to start adding mkfs/fsck tools for every (or any) + filesystem under the sun. (fsck_minix.c and mkfs_minix.c are living on + borrowed time.) There are far too many of these tools out there. Use + the upstream version. Rationale: bugs in these tools can destroy + vast amounts of data. Keeping up with filesystem format development + is impractical (especially in the area of keeping fsck tool safe + and up-to-date). + + - Any disk, device, or media-specific tools: Use the -utils or -tools package + that was designed for your device; don't try to shoehorn them into Busybox. + + - Any architecture specific tools: Busybox is (or should be) architecture + independent. Do not send us tools that cannot be used across multiple + platforms / arches. + + +Bug Reporting +~~~~~~~~~~~~~ + +If you find bugs, please submit a detailed bug report to the busybox mailing +list at busybox@busybox.net. A well-written bug report should include a +transcript of a shell session that demonstrates the bad behavior and enables +anyone else to duplicate the bug on their own machine. The following is such +an example: + + To: busybox@busybox.net + From: diligent@testing.linux.org + Subject: /bin/date doesn't work + + Package: busybox + Version: 1.00 + + When I execute Busybox 'date' it produces unexpected results. + With GNU date I get the following output: + + $ date + Wed Mar 21 14:19:41 MST 2001 + + But when I use BusyBox date I get this instead: + + $ date + Illegal instruction + + I am using Debian unstable, kernel version 2.4.19-rmk1 on an Netwinder, + and the latest uClibc from CVS. + + -Diligent + +Note the careful description and use of examples showing not only what BusyBox +does, but also a counter example showing what an equivalent GNU app does. Bug +reports lacking such detail may never be fixed... Thanks for understanding. + + + +Write Documentation +~~~~~~~~~~~~~~~~~~~ + +Chances are, documentation in Busybox is either missing or needs improvement. +Either way, help is welcome. + +Work is being done to automatically generate documentation from sources, +especially from the usage.h file. If you want to correct the documentation, +please make changes to the pre-generation parts, rather than the generated +documentation. [More to come on this later...] + +It is preferred that modifications to documentation be submitted in patch +format (more on this below), but we're a little more lenient when it comes to +docs. You could, for example, just say "after the listing of the mount +options, the following example would be helpful..." + + +Consult Existing Sources +~~~~~~~~~~~~~~~~~~~~~~~~ + +For a quick listing of "needs work" spots in the sources, cd into the Busybox +directory and run the following: + + for i in TODO FIXME XXX; do find -name '*.[ch]'|xargs grep $i; done + +This will show all of the trouble spots or 'questionable' code. Pick a spot, +any spot, these are all invitations for you to contribute. + + +Add a New Applet +~~~~~~~~~~~~~~~~ + +If you want to add a new applet to Busybox, we'd love to see it. However, +before you write any code, please ask beforehand on the mailing list something +like "Do you think applet 'foo' would be useful in Busybox?" or "Would you +guys accept applet 'foo' into Busybox if I were to write it?" If the answer is +"no" by the folks on the mailing list, then you've saved yourself some time. +Conversely, you could get some positive responses from folks who might be +interested in helping you implement it, or can recommend the best approach. +Perhaps most importantly, this is your way of calling "dibs" on something and +avoiding duplication of effort. + +Also, before you write a line of code, please read the 'new-applet-HOWTO.txt' +file in the docs/ directory. + + +Janitorial Work +~~~~~~~~~~~~~~~ + +These are dirty jobs, but somebody's gotta do 'em. + + - Security audits: + http://www.securityfocus.com/popups/forums/secprog/intro.shtml + + - Synthetic code removal: http://www.perl.com/pub/2000/06/commify.html - This + is very Perl-specific, but the advice given in here applies equally well to + C. + + - C library function use audits: Verifying that functions are being used + properly (called with the right args), replacing unsafe library functions + with safer versions, making sure return codes are being checked, etc. + + - Where appropriate, replace preprocessor defined macros and values with + compile-time equivalents. + + - Style guide compliance. See: docs/style-guide.txt + + - Add testcases to tests/testcases. + + - Makefile improvements: + http://www.canb.auug.org.au/~millerp/rmch/recu-make-cons-harm.html + (I think the recursive problems are pretty much taken care of at this point, non?) + + - "Ten Commandments" compliance: (this is a "maybe", certainly not as + important as any of the previous items.) + http://www.lysator.liu.se/c/ten-commandments.html + +Other useful links: + + - the comp.lang.c FAQ: http://home.datacomm.ch/t_wolf/tw/c/index.html#Sources + + + +Submitting Patches To Busybox +----------------------------- + +Here are some guidelines on how to submit a patch to Busybox. + + +Making A Patch +~~~~~~~~~~~~~~ + +If you've got anonymous Git access set up, making a patch is simple. Just make +sure you're in the busybox/ directory and type: + + git diff -b -w > mychanges.patch + +You can send the resulting .patch file to the mailing list with a description +of what it does. (But not before you test it! See the next section for some +guidelines.) It is preferred that patches be sent as attachments, but it is +not required. + +Also, feel free to help test other people's patches and reply to them with +comments. You can apply a patch by saving it into your busybox/ directory and +typing: + + patch -p1 < mychanges.patch + +Then you can recompile, see if it runs, test if it works as advertised, and +post your findings to the mailing list. + +NOTE: Please do not include extraneous or irrelevant changes in your patches. +Please do not try to "bundle" two patches together into one. Make single, +discreet changes on a per-patch basis. Sometimes you need to make a patch that +touches code in many places, but these kind of patches are rare and should be +coordinated with a maintainer. + + +Testing Guidelines +~~~~~~~~~~~~~~~~~~ + +It's considered good form to test your new feature before you submit a patch +to the mailing list, and especially before you push a change to Git. Here +are some guidelines on how to test your changes. + + - Always test Busybox applets against GNU counterparts and make sure the + behavior / output is identical between the two. + + - Try several different permutations and combinations of the features you're + adding (i.e., different combinations of command-line switches) and make sure + they all work; make sure one feature does not interfere with another. + + - Make sure you test compiling against the source both with the feature + turned on and turned off in Config.h and make sure Busybox compiles cleanly + both ways. + + - Run the multibuild.pl script in the tests directory and make sure + everything checks out OK. (Do this from within the busybox/ directory by + typing: 'tests/multibuild.pl'.) + + +Making Sure Your Patch Doesn't Get Lost +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you don't want your patch to be lost or forgotten, send it to the busybox +mailing list with a subject line something like this: + + [PATCH] - Adds "transmogrify" feature to "foo" + +In the body, you should have a pseudo-header that looks like the following: + + Package: busybox + Version: v1.01pre (or whatever the current version is) + Severity: wishlist + +The remainder of the body should read along these lines: + + This patch adds the "transmogrify" feature to the "foo" applet. I have + tested this on [arch] system(s) and it works. I have tested it against the + GNU counterparts and the outputs are identical. I have run the scripts in + the 'tests' directory and nothing breaks. + + + +Improving Your Chances of Patch Acceptance +------------------------------------------ + +Even after you send a brilliant patch to the mailing list, sometimes it can go +unnoticed, un-replied-to, and sometimes (sigh) even lost. This is an +unfortunate fact of life, but there are steps you can take to help your patch +get noticed and convince a maintainer that it should be added: + + +Be Succinct +~~~~~~~~~~~ + +A patch that includes small, isolated, obvious changes is more likely to be +accepted than a patch that touches code in lots of different places or makes +sweeping, dubious changes. + + +Back It Up +~~~~~~~~~~ + +Hard facts on why your patch is better than the existing code will go a long +way toward convincing maintainers that your patch should be included. +Specifically, patches are more likely to be accepted if they are provably more +correct, smaller, faster, simpler, or more maintainable than the existing +code. + +Conversely, any patch that is supported with nothing more than "I think this +would be cool" or "this patch is good because I say it is and I've got a Phd +in Computer Science" will likely be ignored. + + +Follow The Style Guide +~~~~~~~~~~~~~~~~~~~~~~ + +It's considered good form to abide by the established coding style used in a +project; Busybox is no exception. We have gone so far as to delineate the +"elements of Busybox style" in the file docs/style-guide.txt. Please follow +them. + + +Work With Someone Else +~~~~~~~~~~~~~~~~~~~~~~ + +Working on a patch in isolation is less effective than working with someone +else for a variety of reasons. If another Busybox user is interested in what +you're doing, then it's two (or more) voices instead of one that can petition +for inclusion of the patch. You'll also have more people that can test your +changes, or even offer suggestions on better approaches you could take. + +Getting other folks interested follows as a natural course if you've received +responses from queries to applet maintainer or positive responses from folks +on the mailing list. + +We've made strident efforts to put a useful "collaboration" infrastructure in +place in the form of mailing lists, the bug tracking system, and Git. Please +use these resources. + + +Send Patches to the Bug Tracking System +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This was mentioned above in the "Making Sure Your Patch Doesn't Get Lost" +section, but it is worth mentioning again. A patch sent to the mailing list +might be unnoticed and forgotten. A patch sent to the bug tracking system will +be stored and closely connected to the bug it fixes. + + +Be Polite +~~~~~~~~~ + +The old saying "You'll catch more flies with honey than you will with vinegar" +applies when submitting patches to the mailing list for approval. The way you +present your patch is sometimes just as important as the actual patch itself +(if not more so). Being rude to the maintainers is not an effective way to +convince them that your patch should be included; it will likely have the +opposite effect. + + + +Pushing Changes to Git +---------------------- + +If you submit several patches that demonstrate that you are a skilled and wise +coder, you may be invited to become a committer, thus enabling you to push +changes directly to Git. This is nice because you don't have to wait for +someone else to push your change for you, you can just do it yourself. + +But note that this is a privilege that comes with some responsibilities. You +should test your changes before you push them. You should also talk to an +applet maintainer before you make any kind of sweeping changes to somebody +else's code. Big changes should still go to the mailing list first. Remember, +being wise, polite, and discreet is more important than being clever. + +For more information on Git push access, see: + + http://busybox.net/developer.html + + +When To Push +~~~~~~~~~~~~ + +Generally, you should feel free to push a change if: + + - Your changes are small and don't touch many files + - You are fixing a bug + - Somebody has told you that it's okay + - It's obviously the Right Thing + +The more of the above are true, the better it is to just push a change +directly to Git. + + +When Not To Push +~~~~~~~~~~~~~~~~ + +Even if you have push access, you should probably still post a patch to the +mailing list if: + + - Your changes are broad and touch many different files + - You are adding a feature + - Your changes are speculative or experimental (i.e., trying a new algorithm) + - You are not the maintainer and your changes make the maintainer cringe + +The more of the above are true, the better it is to post a patch to the +mailing list instead of pushing. + + + +Final Words +----------- + +If all of this seems complicated, don't panic, it's really not that tough. If +you're having difficulty following some of the steps outlined in this +document don't worry, the folks on the Busybox mailing list are a fairly +good-natured bunch and will work with you to help get your patches into shape +or help you make contributions. diff --git a/busybox/docs/ctty.htm b/busybox/docs/ctty.htm new file mode 100644 index 00000000000000..3cb2dd2bdba88d --- /dev/null +++ b/busybox/docs/ctty.htm @@ -0,0 +1,479 @@ + + + + The Linux kernel: Processes + + +
+

10. Processes

+ +

Before looking at the Linux implementation, first a general Unix +description of threads, processes, process groups and sessions. +

+(See also General Terminal Interface) +

A session contains a number of process groups, and a process group +contains a number of processes, and a process contains a number +of threads. +

A session can have a controlling tty. +At most one process group in a session can be a foreground process group. +An interrupt character typed on a tty ("Teletype", i.e., terminal) +causes a signal to be sent to all members of the foreground process group +in the session (if any) that has that tty as controlling tty. +

All these objects have numbers, and we have thread IDs, process IDs, +process group IDs and session IDs. +

+

10.1 Processes +

+ +

+

Creation

+ +

A new process is traditionally started using the fork() +system call: +

+
pid_t p;
+
+p = fork();
+if (p == (pid_t) -1)
+        /* ERROR */
+else if (p == 0)
+        /* CHILD */
+else
+        /* PARENT */
+
+
+

This creates a child as a duplicate of its parent. +Parent and child are identical in almost all respects. +In the code they are distinguished by the fact that the parent +learns the process ID of its child, while fork() +returns 0 in the child. (It can find the process ID of its +parent using the getppid() system call.) +

+

Termination

+ +

Normal termination is when the process does +

+
exit(n);
+
+
+ +or +
+
return n;
+
+
+ +from its main() procedure. It returns the single byte n +to its parent. +

Abnormal termination is usually caused by a signal. +

+

Collecting the exit code. Zombies

+ +

The parent does +

+
pid_t p;
+int status;
+
+p = wait(&status);
+
+
+ +and collects two bytes: +

+

+ + + +

A process that has terminated but has not yet been waited for +is a zombie. It need only store these two bytes: +exit code and reason for termination. +

On the other hand, if the parent dies first, init (process 1) +inherits the child and becomes its parent. +

+

Signals

+ +

+

Stopping

+ +

Some signals cause a process to stop: +SIGSTOP (stop!), +SIGTSTP (stop from tty: probably ^Z was typed), +SIGTTIN (tty input asked by background process), +SIGTTOU (tty output sent by background process, and this was +disallowed by stty tostop). +

Apart from ^Z there also is ^Y. The former stops the process +when it is typed, the latter stops it when it is read. +

Signals generated by typing the corresponding character on some tty +are sent to all processes that are in the foreground process group +of the session that has that tty as controlling tty. (Details below.) +

If a process is being traced, every signal will stop it. +

+

Continuing

+ +

SIGCONT: continue a stopped process. +

+

Terminating

+ +

SIGKILL (die! now!), +SIGTERM (please, go away), +SIGHUP (modem hangup), +SIGINT (^C), +SIGQUIT (^\), etc. +Many signals have as default action to kill the target. +(Sometimes with an additional core dump, when such is +allowed by rlimit.) +The signals SIGCHLD and SIGWINCH +are ignored by default. +All except SIGKILL and SIGSTOP can be +caught or ignored or blocked. +For details, see signal(7). +

+

10.2 Process groups +

+ +

Every process is member of a unique process group, +identified by its process group ID. +(When the process is created, it becomes a member of the process group +of its parent.) +By convention, the process group ID of a process group +equals the process ID of the first member of the process group, +called the process group leader. +A process finds the ID of its process group using the system call +getpgrp(), or, equivalently, getpgid(0). +One finds the process group ID of process p using +getpgid(p). +

One may use the command ps j to see PPID (parent process ID), +PID (process ID), PGID (process group ID) and SID (session ID) +of processes. With a shell that does not know about job control, +like ash, each of its children will be in the same session +and have the same process group as the shell. With a shell that knows +about job control, like bash, the processes of one pipeline, like +

+
% cat paper | ideal | pic | tbl | eqn | ditroff > out
+
+
+ +form a single process group. +

+

Creation

+ +

A process pid is put into the process group pgid by +

+
setpgid(pid, pgid);
+
+
+ +If pgid == pid or pgid == 0 then this creates +a new process group with process group leader pid. +Otherwise, this puts pid into the already existing +process group pgid. +A zero pid refers to the current process. +The call setpgrp() is equivalent to setpgid(0,0). +

+

Restrictions on setpgid()

+ +

The calling process must be pid itself, or its parent, +and the parent can only do this before pid has done +exec(), and only when both belong to the same session. +It is an error if process pid is a session leader +(and this call would change its pgid). +

+

Typical sequence

+ +

+

+
p = fork();
+if (p == (pid_t) -1) {
+        /* ERROR */
+} else if (p == 0) {    /* CHILD */
+        setpgid(0, pgid);
+        ...
+} else {                /* PARENT */
+        setpgid(p, pgid);
+        ...
+}
+
+
+ +This ensures that regardless of whether parent or child is scheduled +first, the process group setting is as expected by both. +

+

Signalling and waiting

+ +

One can signal all members of a process group: +

+
killpg(pgrp, sig);
+
+
+

One can wait for children in ones own process group: +

+
waitpid(0, &status, ...);
+
+
+ +or in a specified process group: +
+
waitpid(-pgrp, &status, ...);
+
+
+

+

Foreground process group

+ +

Among the process groups in a session at most one can be +the foreground process group of that session. +The tty input and tty signals (signals generated by ^C, ^Z, etc.) +go to processes in this foreground process group. +

A process can determine the foreground process group in its session +using tcgetpgrp(fd), where fd refers to its +controlling tty. If there is none, this returns a random value +larger than 1 that is not a process group ID. +

A process can set the foreground process group in its session +using tcsetpgrp(fd,pgrp), where fd refers to its +controlling tty, and pgrp is a process group in +its session, and this session still is associated to the controlling +tty of the calling process. +

How does one get fd? By definition, /dev/tty +refers to the controlling tty, entirely independent of redirects +of standard input and output. (There is also the function +ctermid() to get the name of the controlling terminal. +On a POSIX standard system it will return /dev/tty.) +Opening the name of the +controlling tty gives a file descriptor fd. +

+

Background process groups

+ +

All process groups in a session that are not foreground +process group are background process groups. +Since the user at the keyboard is interacting with foreground +processes, background processes should stay away from it. +When a background process reads from the terminal it gets +a SIGTTIN signal. Normally, that will stop it, the job control shell +notices and tells the user, who can say fg to continue +this background process as a foreground process, and then this +process can read from the terminal. But if the background process +ignores or blocks the SIGTTIN signal, or if its process group +is orphaned (see below), then the read() returns an EIO error, +and no signal is sent. (Indeed, the idea is to tell the process +that reading from the terminal is not allowed right now. +If it wouldn't see the signal, then it will see the error return.) +

When a background process writes to the terminal, it may get +a SIGTTOU signal. May: namely, when the flag that this must happen +is set (it is off by default). One can set the flag by +

+
% stty tostop
+
+
+ +and clear it again by +
+
% stty -tostop
+
+
+ +and inspect it by +
+
% stty -a
+
+
+ +Again, if TOSTOP is set but the background process ignores or blocks +the SIGTTOU signal, or if its process group is orphaned (see below), +then the write() returns an EIO error, and no signal is sent. +[vda: correction. SUS says that if SIGTTOU is blocked/ignored, write succeeds. ] +

+

Orphaned process groups

+ +

The process group leader is the first member of the process group. +It may terminate before the others, and then the process group is +without leader. +

A process group is called orphaned when the +parent of every member is either in the process group +or outside the session. +In particular, the process group of the session leader +is always orphaned. +

If termination of a process causes a process group to become +orphaned, and some member is stopped, then all are sent first SIGHUP +and then SIGCONT. +

The idea is that perhaps the parent of the process group leader +is a job control shell. (In the same session but a different +process group.) As long as this parent is alive, it can +handle the stopping and starting of members in the process group. +When it dies, there may be nobody to continue stopped processes. +Therefore, these stopped processes are sent SIGHUP, so that they +die unless they catch or ignore it, and then SIGCONT to continue them. +

Note that the process group of the session leader is already +orphaned, so no signals are sent when the session leader dies. +

Note also that a process group can become orphaned in two ways +by termination of a process: either it was a parent and not itself +in the process group, or it was the last element of the process group +with a parent outside but in the same session. +Furthermore, that a process group can become orphaned +other than by termination of a process, namely when some +member is moved to a different process group. +

+

10.3 Sessions +

+ +

Every process group is in a unique session. +(When the process is created, it becomes a member of the session +of its parent.) +By convention, the session ID of a session +equals the process ID of the first member of the session, +called the session leader. +A process finds the ID of its session using the system call +getsid(). +

Every session may have a controlling tty, +that then also is called the controlling tty of each of +its member processes. +A file descriptor for the controlling tty is obtained by +opening /dev/tty. (And when that fails, there was no +controlling tty.) Given a file descriptor for the controlling tty, +one may obtain the SID using tcgetsid(fd). +

A session is often set up by a login process. The terminal +on which one is logged in then becomes the controlling tty +of the session. All processes that are descendants of the +login process will in general be members of the session. +

+

Creation

+ +

A new session is created by +

+
pid = setsid();
+
+
+ +This is allowed only when the current process is not a process group leader. +In order to be sure of that we fork first: +
+
p = fork();
+if (p) exit(0);
+pid = setsid();
+
+
+ +The result is that the current process (with process ID pid) +becomes session leader of a new session with session ID pid. +Moreover, it becomes process group leader of a new process group. +Both session and process group contain only the single process pid. +Furthermore, this process has no controlling tty. +

The restriction that the current process must not be a process group leader +is needed: otherwise its PID serves as PGID of some existing process group +and cannot be used as the PGID of a new process group. +

+

Getting a controlling tty

+ +

How does one get a controlling terminal? Nobody knows, +this is a great mystery. +

The System V approach is that the first tty opened by the process +becomes its controlling tty. +

The BSD approach is that one has to explicitly call +

+
ioctl(fd, TIOCSCTTY, 0/1);
+
+
+ +to get a controlling tty. +

Linux tries to be compatible with both, as always, and this +results in a very obscure complex of conditions. Roughly: +

The TIOCSCTTY ioctl will give us a controlling tty, +provided that (i) the current process is a session leader, +and (ii) it does not yet have a controlling tty, and +(iii) maybe the tty should not already control some other session; +if it does it is an error if we aren't root, or we steal the tty +if we are all-powerful. +[vda: correction: third parameter controls this: if 1, we steal tty from +any such session, if 0, we don't steal] +

Opening some terminal will give us a controlling tty, +provided that (i) the current process is a session leader, and +(ii) it does not yet have a controlling tty, and +(iii) the tty does not already control some other session, and +(iv) the open did not have the O_NOCTTY flag, and +(v) the tty is not the foreground VT, and +(vi) the tty is not the console, and +(vii) maybe the tty should not be master or slave pty. +

+

Getting rid of a controlling tty

+ +

If a process wants to continue as a daemon, it must detach itself +from its controlling tty. Above we saw that setsid() +will remove the controlling tty. Also the ioctl TIOCNOTTY does this. +Moreover, in order not to get a controlling tty again as soon as it +opens a tty, the process has to fork once more, to assure that it +is not a session leader. Typical code fragment: +

+

        if ((fork()) != 0)
+                exit(0);
+        setsid();
+        if ((fork()) != 0)
+                exit(0);
+
+

See also daemon(3). +

+

Disconnect

+ +

If the terminal goes away by modem hangup, and the line was not local, +then a SIGHUP is sent to the session leader. +Any further reads from the gone terminal return EOF. +(Or possibly -1 with errno set to EIO.) +

If the terminal is the slave side of a pseudotty, and the master side +is closed (for the last time), then a SIGHUP is sent to the foreground +process group of the slave side. +

When the session leader dies, a SIGHUP is sent to all processes +in the foreground process group. Moreover, the terminal stops being +the controlling terminal of this session (so that it can become +the controlling terminal of another session). +

Thus, if the terminal goes away and the session leader is +a job control shell, then it can handle things for its descendants, +e.g. by sending them again a SIGHUP. +If on the other hand the session leader is an innocent process +that does not catch SIGHUP, it will die, and all foreground processes +get a SIGHUP. +

+

10.4 Threads +

+ +

A process can have several threads. New threads (with the same PID +as the parent thread) are started using the clone system +call using the CLONE_THREAD flag. Threads are distinguished +by a thread ID (TID). An ordinary process has a single thread +with TID equal to PID. The system call gettid() returns the +TID. The system call tkill() sends a signal to a single thread. +

Example: a process with two threads. Both only print PID and TID and exit. +(Linux 2.4.19 or later.) +

% cat << EOF > gettid-demo.c
+#include <unistd.h>
+#include <sys/types.h>
+#define CLONE_SIGHAND   0x00000800
+#define CLONE_THREAD    0x00010000
+#include <linux/unistd.h>
+#include <errno.h>
+_syscall0(pid_t,gettid)
+
+int thread(void *p) {
+        printf("thread: %d %d\n", gettid(), getpid());
+}
+
+main() {
+        unsigned char stack[4096];
+        int i;
+
+        i = clone(thread, stack+2048, CLONE_THREAD | CLONE_SIGHAND, NULL);
+        if (i == -1)
+                perror("clone");
+        else
+                printf("clone returns %d\n", i);
+        printf("parent: %d %d\n", gettid(), getpid());
+}
+EOF
+% cc -o gettid-demo gettid-demo.c
+% ./gettid-demo
+clone returns 21826
+parent: 21825 21825
+thread: 21826 21825
+%
+
+

+

+


+ + diff --git a/busybox/docs/draft-coar-cgi-v11-03-clean.html b/busybox/docs/draft-coar-cgi-v11-03-clean.html new file mode 100644 index 00000000000000..d52c9b842cd5fa --- /dev/null +++ b/busybox/docs/draft-coar-cgi-v11-03-clean.html @@ -0,0 +1,2674 @@ + + + + Common Gateway Interface - 1.1 *Draft 03* [http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html] + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ INTERNET-DRAFT                                 + +           Ken A L Coar +
+ draft-coar-cgi-v11-03.{html,txt}              + +         IBM Corporation +
+                                                 + +       D.R.T. Robinson +
+                                                 + +       E*TRADE UK Ltd. +
+                                                 + +         25 June 1999 +
+
+ +

+ The WWW Common Gateway Interface +
+ Version 1.1 +

+ + + +

+ + Abstract + +

+

+ The Common Gateway Interface (CGI) is a simple interface for running + external programs, software or gateways under an information server + in a platform-independent manner. Currently, the supported information + servers are HTTP servers. +

+

+ The interface has been in use by the World-Wide Web since 1993. This + specification defines the + "current practice" parameters of the + 'CGI/1.1' interface developed and documented at the U.S. National + Centre for Supercomputing Applications [NCSA-CGI]. + This document also defines the use of the CGI/1.1 interface + on the Unix and AmigaDOS(tm) systems. +

+

+ Discussion of this draft occurs on the CGI-WG mailing list; see the + project Web page at + <URL:http://CGI-Spec.Golux.Com/> + for details on the mailing list and the status of the project. +

+ + +

+ Revision History +

+

+ The revision history of this draft is being maintained using Web-based + GUI notation, such as struck-through characters and colour-coded + sections. The following legend describes how to determine the origin + of a particular revision according to the colour of the text: +

+
+
Black +
+
Revision 00, released 28 May 1998 +
+
Green +
+
Revision 01, released 28 December 1998 +
+ Major structure change: Section 4, "Request Metadata (Meta-Variables)" + was moved entirely under Section 7, "Data Input to the + CGI Script." + Due to the size of this change, it is noted here and the text in its + former location does not appear as struckthrough. This has + caused major sections 5 and following to decrement + by one. Other + large text movements are likewise not marked up. References to RFC + 1738 were changed to 2396 (1738's replacement). +
+
Red +
+
Revision 02, released 2 April, 1999 +
+ Added text to section 8.3 defining correct handling + of HTTP/1.1 + requests using "chunked" Transfer-Encoding. Labelled metavariable + names in section 8 with the appropriate detail section + numbers. + Clarified allowed usage of Status and + Location response header fields. Included new + Internet-Draft language. +
+
Fuchsia +
+
Revision 03, released 25 June 1999 +
+ Changed references from "HTTP" to "Protocol-Specific" for the listing of + things like HTTP_ACCEPT. Changed 'entity-body' and 'content-body' to + 'message-body.' Added a note that response headers must comply with + requirements of the protocol level in use. Added a lot of stuff about + security (section 11). Clarified a bunch of productions. Pointed out + that zero-length and omitted values are indistinguishable in this + specification. Clarified production describing order of fields in + script response header. Clarified issues surrounding encoding of + data. Acknowledged additional contributors, and changed one of + the authors' addresses. +
+
+ + +

+ + Table of Contents + +

+
+
+  1 Introduction..............................................TBD
+   1.1 Purpose................................................TBD
+   1.2 Requirements...........................................TBD
+   1.3 Specifications.........................................TBD
+   1.4 Terminology............................................TBD
+  2 Notational Conventions and Generic Grammar................TBD
+   2.1 Augmented BNF..........................................TBD
+   2.2 Basic Rules............................................TBD
+  3 Protocol Parameters.......................................TBD
+   3.1 URL Encoding...........................................TBD
+   3.2 The Script-URI.........................................TBD
+  4 Invoking the Script.......................................TBD
+  5 The CGI Script Command Line...............................TBD
+  6 Data Input to the CGI Script..............................TBD
+   6.1 Request Metadata (Metavariables).......................TBD
+    6.1.1 AUTH_TYPE...........................................TBD
+    6.1.2 CONTENT_LENGTH......................................TBD
+    6.1.3 CONTENT_TYPE........................................TBD
+    6.1.4 GATEWAY_INTERFACE...................................TBD
+    6.1.5 Protocol-Specific Metavariables.....................TBD
+    6.1.6 PATH_INFO...........................................TBD
+    6.1.7 PATH_TRANSLATED.....................................TBD
+    6.1.8 QUERY_STRING........................................TBD
+    6.1.9 REMOTE_ADDR.........................................TBD
+    6.1.10 REMOTE_HOST........................................TBD
+    6.1.11 REMOTE_IDENT.......................................TBD
+    6.1.12 REMOTE_USER........................................TBD
+    6.1.13 REQUEST_METHOD.....................................TBD
+    6.1.14 SCRIPT_NAME........................................TBD
+    6.1.15 SERVER_NAME........................................TBD
+    6.1.16 SERVER_PORT........................................TBD
+    6.1.17 SERVER_PROTOCOL....................................TBD
+    6.1.18 SERVER_SOFTWARE....................................TBD
+    6.2 Request Message-Bodies................................TBD
+  7 Data Output from the CGI Script...........................TBD
+   7.1 Non-Parsed Header Output...............................TBD
+   7.2 Parsed Header Output...................................TBD
+    7.2.1 CGI header fields...................................TBD
+     7.2.1.1 Content-Type.....................................TBD
+     7.2.1.2 Location.........................................TBD
+     7.2.1.3 Status...........................................TBD
+     7.2.1.4 Extension header fields..........................TBD
+    7.2.2 HTTP header fields..................................TBD
+  8 Server Implementation.....................................TBD
+   8.1 Requirements for Servers...............................TBD
+    8.1.1 Script-URI..........................................TBD
+    8.1.2 Request Message-body Handling.......................TBD
+    8.1.3 Required Metavariables..............................TBD
+    8.1.4 Response Compliance.................................TBD
+   8.2 Recommendations for Servers............................TBD
+   8.3 Summary of Metavariables...............................TBD
+  9 Script Implementation.....................................TBD
+   9.1 Requirements for Scripts...............................TBD
+   9.2 Recommendations for Scripts............................TBD
+  10 System Specifications....................................TBD
+   10.1 AmigaDOS..............................................TBD
+   10.2 Unix..................................................TBD
+  11 Security Considerations..................................TBD
+   11.1 Safe Methods..........................................TBD
+   11.2 HTTP Header Fields Containing Sensitive Information...TBD
+   11.3 Script Interference with the Server...................TBD
+   11.4 Data Length and Buffering Considerations..............TBD
+   11.5 Stateless Processing..................................TBD
+  12 Acknowledgments..........................................TBD
+  13 References...............................................TBD
+  14 Authors' Addresses.......................................TBD
+     
+
+ +

+ + 1. Introduction + +

+ +

+ + 1.1. Purpose + +

+

+ Together the HTTP [3,8] server + and the CGI script are responsible + for servicing a client + request by sending back responses. The client + request comprises a Universal Resource Identifier (URI) + [1], a + request method, and various ancillary + information about the request + provided by the transport mechanism. +

+

+ The CGI defines the abstract parameters, known as + metavariables, + which describe the client's + request. Together with a + concrete programmer interface this specifies a platform-independent + interface between the script and the HTTP server. +

+ +

+ + 1.2. Requirements + +

+

+ This specification uses the same words as RFC 1123 + [5] to define the + significance of each particular requirement. These are: +

+

+
+
MUST +
+
+

+ This word or the adjective 'required' means that the item is an + absolute requirement of the specification. +

+
+
SHOULD +
+
+

+ This word or the adjective 'recommended' means that there may + exist valid reasons in particular circumstances to ignore this + item, but the full implications should be understood and the case + carefully weighed before choosing a different course. +

+
+
MAY +
+
+

+ This word or the adjective 'optional' means that this item is + truly optional. One vendor may choose to include the item because + a particular marketplace requires it or because it enhances the + product, for example; another vendor may omit the same item. +

+
+
+

+ An implementation is not compliant if it fails to satisfy one or more + of the 'must' requirements for the protocols it implements. An + implementation that satisfies all of the 'must' and all of the + 'should' requirements for its features is said to be 'unconditionally + compliant'; one that satisfies all of the 'must' requirements but not + all of the 'should' requirements for its features is said to be + 'conditionally compliant.' +

+ +

+ + 1.3. Specifications + +

+

+ Not all of the functions and features of the CGI are defined in the + main part of this specification. The following phrases are used to + describe the features which are not specified: +

+
+
system defined +
+
+

+ The feature may differ between systems, but must be the same for + different implementations using the same system. A system will + usually identify a class of operating-systems. Some systems are + defined in + section 10 of this document. + New systems may be defined + by new specifications without revision of this document. +

+
+
implementation defined +
+
+

+ The behaviour of the feature may vary from implementation to + implementation, but a particular implementation must document its + behaviour. +

+
+
+ +

+ + 1.4. Terminology + +

+

+ This specification uses many terms defined in the HTTP/1.1 + specification [8]; however, the following terms are + used here in a + sense which may not accord with their definitions in that document, + or with their common meaning. +

+ +
+
metavariable +
+
+

+ A named parameter that carries information from the server to the + script. It is not necessarily a variable in the operating-system's + environment, although that is the most common implementation. +

+
+ +
script +
+
+

+ The software which is invoked by the server via this + interface. It + need not be a standalone program, but could be a + dynamically-loaded or shared library, or even a subroutine in the + server. It may be a set of statements + interpreted at run-time, as the term 'script' is frequently + understood, but that is not a requirement and within the context + of this specification the term has the broader definition stated. +

+
+
server +
+
+

+ The application program which invokes the script in order to service + requests. +

+
+
+ +

+ + 2. Notational Conventions and Generic Grammar + +

+ +

+ + 2.1. Augmented BNF + +

+

+ All of the mechanisms specified in this document are described in + both prose and an augmented Backus-Naur Form (BNF) similar to that + used by RFC 822 [6]. This augmented BNF contains + the following constructs: +

+
+
name = definition +
+
+

+ The + definition by the equal character ("="). Whitespace is only + significant in that continuation lines of a definition are + indented. +

+
+
"literal" +
+
+

+ Quotation marks (") surround literal text, except for a literal + quotation mark, which is surrounded by angle-brackets ("<" and ">"). + Unless stated otherwise, the text is case-sensitive. +

+
+
rule1 | rule2 +
+
+

+ Alternative rules are separated by a vertical bar ("|"). +

+
+
(rule1 rule2 rule3) +
+
+

+ Elements enclosed in parentheses are treated as a single element. +

+
+
*rule +
+
+

+ A rule preceded by an asterisk ("*") may have zero or more + occurrences. A rule preceded by an integer followed by an asterisk + must occur at least the specified number of times. +

+
+
[rule] +
+
+

+ An element enclosed in square + brackets ("[" and "]") is optional. +

+
+
+ +

+ + 2.2. Basic Rules + +

+

+ The following rules are used throughout this specification to + describe basic parsing constructs. +

+

+
+    alpha         = lowalpha | hialpha
+    alphanum      = alpha | digit
+    lowalpha      = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h"
+                    | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p"
+                    | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x"
+                    | "y" | "z"
+    hialpha       = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H"
+                    | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P"
+                    | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X"
+                    | "Y" | "Z"
+    digit         = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7"
+                    | "8" | "9"
+    hex           = digit | "A" | "B" | "C" | "D" | "E" | "F" | "a"
+                    | "b" | "c" | "d" | "e" | "f"
+    escaped       = "%" hex hex
+    OCTET         = <any 8-bit sequence of data>
+    CHAR          = <any US-ASCII character (octets 0 - 127)>
+    CTL           = <any US-ASCII control character
+                    (octets 0 - 31) and DEL (127)>
+    CR            = <US-ASCII CR, carriage return (13)>
+    LF            = <US-ASCII LF, linefeed (10)>
+    SP            = <US-ASCII SP, space (32)>
+    HT            = <US-ASCII HT, horizontal tab (9)>
+    NL            = CR | LF
+    LWSP          = SP | HT | NL
+    tspecial      = "(" | ")" | "@" | "," | ";" | ":" | "\" | <">
+                    | "/" | "[" | "]" | "?" | "<" | ">" | "{" | "}"
+                    | SP | HT | NL
+    token         = 1*<any CHAR except CTLs or tspecials>
+    quoted-string = ( <"> *qdtext <"> ) | ( "<" *qatext ">")
+    qdtext        = <any CHAR except <"> and CTLs but including LWSP>
+    qatext        = <any CHAR except "<", ">" and CTLs but
+                    including LWSP>
+    mark          = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+    unreserved    = alphanum | mark
+    reserved      = ";" | "/" | "?" | ":" | "@" | "&" | "=" |
+                    "$" | ","
+    uric          = reserved | unreserved | escaped
+  
+

+ Note that newline (NL) need not be a single character, but can be a + character sequence. +

+ +

+ + 3. Protocol Parameters + +

+ +

+ + 3.1. URL Encoding + +

+

+ Some variables and constructs used here are described as being + 'URL-encoded'. This encoding is described in section + 2 of RFC + 2396 + [4]. +

+

+ An alternate "shortcut" encoding for representing the space + character exists and is in common use. Scripts MUST be prepared to + recognise both '+' and '%20' as an encoded space in a + URL-encoded value. +

+

+ Note that some unsafe characters may have different semantics if + they are encoded. The definition of which characters are unsafe + depends on the context. + For example, the following two URLs do not + necessarily refer to the same resource: +

+

+
+    http://somehost.com/somedir%2Fvalue
+    http://somehost.com/somedir/value
+  
+

+ See section + 2 of RFC + 2396 [4] + for authoritative treatment of this issue. +

+ +

+ + 3.2. The Script-URI + +

+

+ The 'Script-URI' is defined as the URI of the resource identified + by the metavariables. Often, + this URI will be the same as + the URI requested by the client (the 'Client-URI'); however, it need + not be. Instead, it could be a URI invented by the server, and so it + can only be used in the context of the server and its CGI interface. +

+

+ The Script-URI has the syntax of generic-RL as defined in section 2.1 + of RFC 1808 [7], with the exception that object + parameters and + fragment identifiers are not permitted: +

+

+
+    <scheme>://<host><port>/<path>?<query>
+  
+

+ The various components of the + Script-URI + are defined by some of the + metavariables (see + section 4 + below); +

+

+
+    script-uri = protocol "://" SERVER_NAME ":" SERVER_PORT enc-script
+                 enc-path-info "?" QUERY_STRING
+  
+

+ where 'protocol' is obtained + from SERVER_PROTOCOL, 'enc-script' is a + URL-encoded version of SCRIPT_NAME and 'enc-path-info' is a + URL-encoded version of PATH_INFO. See + section 4.6 for more information about the PATH_INFO + metavariable. +

+

+ Note that the scheme and the protocol are not identical; + for instance, a resource accessed via an SSL mechanism + may have a Client-URI with a scheme of "https" + rather than "http". CGI/1.1 provides no means + for the script to reconstruct this, and therefore + the Script-URI includes the base protocol used. +

+ +

+ + 4. Invoking the Script + +

+

+ The + script is invoked in a system defined manner. Unless specified + otherwise, the file containing the script will be invoked as an + executable program. +

+ +

+ + 5. The CGI Script Command Line + +

+

+ Some systems support a method for supplying an array of strings to + the CGI script. This is only used in the case of an 'indexed' query. + This is identified by a "GET" or "HEAD" HTTP request with a URL + query + string not containing any unencoded "=" characters. For such a + request, + servers SHOULD parse the search string + into words, using the following rules: +

+

+
+    search-string = search-word *( "+" search-word )
+    search-word   = 1*schar
+    schar         = xunreserved | escaped | xreserved
+    xunreserved   = alpha | digit | xsafe | extra
+    xsafe         = "$" | "-" | "_" | "."
+    xreserved     = ";" | "/" | "?" | ":" | "@" | "&"
+  
+

+ After parsing, each word is URL-decoded, optionally encoded in a + system defined manner, + and then the argument list is set to the list + of words. +

+

+ If the server cannot create any part of the argument list, then the + server SHOULD NOT generate any command line information. For example, the + number of arguments may be greater than operating system or server + limitations permit, or one of the words may not be representable as an + argument. +

+

+ Scripts SHOULD check to see if the QUERY_STRING value contains an + unencoded "=" character, and SHOULD NOT use the command line arguments + if it does. +

+ +

+ + 6. Data Input to the CGI Script + +

+

+ Information about a request comes from two different sources: the + request header, and any associated + message-body. + Servers MUST + make portions of this information available to + scripts. +

+ +

+ + 6.1. Request Metadata + (Metavariables) + +

+

+ Each CGI server + implementation MUST define a mechanism + to pass data about the request from + the server to the script. + The metavariables containing these + data + are accessed by the script in a system + defined manner. + The + representation of the characters in the + metavariables is + system defined. +

+

+ This specification does not distinguish between the representation of + null values and missing ones. Whether null or missing values + (such as a query component of "?" or "", respectively) are represented + by undefined metavariables or by metavariables with values of "" is + implementation-defined. +

+

+ Case is not significant in the + metavariable + names, in that there cannot be two + different variables + whose names differ in case only. Here they are + shown using a canonical representation of capitals plus underscore + ("_"). The actual representation of the names is system defined; for + a particular system the representation MAY be defined differently + than this. +

+

+ Metavariable + values MUST be + considered case-sensitive except as noted + otherwise. +

+

+ The canonical + metavariables + defined by this specification are: +

+

+
+    AUTH_TYPE
+    CONTENT_LENGTH
+    CONTENT_TYPE
+    GATEWAY_INTERFACE
+    PATH_INFO
+    PATH_TRANSLATED
+    QUERY_STRING
+    REMOTE_ADDR
+    REMOTE_HOST
+    REMOTE_IDENT
+    REMOTE_USER
+    REQUEST_METHOD
+    SCRIPT_NAME
+    SERVER_NAME
+    SERVER_PORT
+    SERVER_PROTOCOL
+    SERVER_SOFTWARE
+  
+

+ Metavariables with names beginning with the protocol name (e.g., + "HTTP_ACCEPT") are also canonical in their description of request header + fields. The number and meaning of these fields may change independently + of this specification. (See also section 6.1.5.) +

+ +

+ + 6.1.1. AUTH_TYPE + +

+

+ This variable is specific to requests made + via the + "http" + scheme. +

+

+ If the Script-URI + required access authentication for external + access, then the server + MUST set + the value of + this variable + from the 'auth-scheme' token in + the request's "Authorization" header + field. + Otherwise + it is + set to NULL. +

+

+
+    AUTH_TYPE   = "" | auth-scheme
+    auth-scheme = "Basic" | "Digest" | token
+  
+

+ HTTP access authentication schemes are described in section 11 of the + HTTP/1.1 specification [8]. The auth-scheme is + not case-sensitive. +

+

+ Servers + MUST + provide this metavariable + to scripts if the request + header included an "Authorization" field + that was authenticated. +

+ +

+ + 6.1.2. CONTENT_LENGTH + +

+

+ This + metavariable + is set to the + size of the message-body + entity attached to the request, if any, in decimal + number of octets. If no data are attached, then this + metavariable + is either NULL or not + defined. The syntax is + the same as for + the HTTP "Content-Length" header field (section 14.14, HTTP/1.1 + specification [8]). +

+

+
+    CONTENT_LENGTH = "" | 1*digit
+  
+

+ Servers MUST provide this metavariable + to scripts if the request + was accompanied by a + message-body entity. +

+ +

+ + 6.1.3. CONTENT_TYPE + +

+

+ If the request includes a + message-body, + CONTENT_TYPE is set + to + the Internet Media Type + [9] of the attached + entity if the type was provided via + a "Content-type" field in the + request header, or if the server can determine it in the absence + of a supplied "Content-type" field. The syntax is the + same as for the HTTP + "Content-Type" header field. +

+

+
+    CONTENT_TYPE = "" | media-type
+    media-type   = type "/" subtype *( ";" parameter)
+    type         = token
+    subtype      = token
+    parameter    = attribute "=" value
+    attribute    = token
+    value        = token | quoted-string
+  
+

+ The type, subtype, + and parameter attribute names are not + case-sensitive. Parameter values MAY be case sensitive. + Media types and their use in HTTP are described + in section 3.7 of the + HTTP/1.1 specification [8]. +

+

+ Example: +

+

+
+    application/x-www-form-urlencoded
+  
+

+ There is no default value for this variable. If and only if it is + unset, then the script MAY attempt to determine the media type from + the data received. If the type remains unknown, then + the script MAY choose to either assume a + content-type of + application/octet-stream + or reject the request with a 415 ("Unsupported Media Type") + error. See section 7.2.1.3 + for more information about returning error status values. +

+

+ Servers MUST provide this metavariable + to scripts if + a "Content-Type" field was present + in the original request header. If the server receives a request + with an attached entity but no "Content-Type" + header field, it MAY attempt to + determine the correct datatype, or it MAY omit this + metavariable when + communicating the request information to the script. +

+ +

+ + 6.1.4. GATEWAY_INTERFACE + +

+

+ This + metavariable + is set to + the dialect of CGI being used + by the server to communicate with the script. + Syntax: +

+

+
+    GATEWAY_INTERFACE = "CGI" "/" major "." minor
+    major             = 1*digit
+    minor             = 1*digit
+  
+

+ Note that the major and minor numbers are treated as separate + integers and hence each may be + more than a single + digit. Thus CGI/2.4 is a lower version than CGI/2.13 which in turn + is lower than CGI/12.3. Leading zeros in either + the major or the minor number MUST be ignored by scripts and + SHOULD NOT be generated by servers. +

+

+ This document defines the 1.1 version of the CGI interface + ("CGI/1.1"). +

+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.5. Protocol-Specific Metavariables + +

+

+ These metavariables are specific to + the protocol + via which the request is made. + Interpretation of these variables depends on the value of + the + SERVER_PROTOCOL + metavariable + (see + section 6.1.17). +

+

+ Metavariables + with names beginning with "HTTP_" contain + values from the request header, if the + scheme used was HTTP. + Each + HTTP header field name is converted to upper case, has all occurrences of + "-" replaced with "_", + and has "HTTP_" prepended to form + the metavariable name. + Similar transformations are applied for other + protocols. + The header data MAY be presented as sent + by the client, or MAY be rewritten in ways which do not change its + semantics. If multiple header fields with the same field-name are received + then the server + MUST rewrite them as though they + had been received as a single header field having the same + semantics before being represented in a + metavariable. + Similarly, a header field that is received on more than one line + MUST be merged into a single line. The server MUST, if necessary, + change the representation of the data (for example, the character + set) to be appropriate for a CGI + metavariable. + +

+

+ Servers are + not required to create + metavariables for all + the request + header fields that they + receive. In particular, + they MAY + decline to make available any + header fields carrying authentication information, such as + "Authorization", or + which are available to the script + via other metavariables, + such as "Content-Length" and "Content-Type". +

+ +

+ + 6.1.6. PATH_INFO + +

+

+ The PATH_INFO + metavariable + specifies + a path to be interpreted by the CGI script. It identifies the + resource or sub-resource to be returned + by the CGI + script, and it is derived from the portion + of the URI path following the script name but preceding + any query data. + The syntax + and semantics are similar to a decoded HTTP URL + 'path' token + (defined in + RFC 2396 + [4]), with the exception + that a PATH_INFO of "/" + represents a single void path segment. +

+

+
+    PATH_INFO = "" | ( "/" path )
+    path      = segment *( "/" segment )
+    segment   = *pchar
+    pchar     = <any CHAR except "/">
+  
+

+ The PATH_INFO string is the trailing part of the <path> component of + the Script-URI + (see section 3.2) + that follows the SCRIPT_NAME + portion of the path. +

+

+ Servers MAY impose their own restrictions and + limitations on what values they will accept for PATH_INFO, and MAY + reject or edit any values they + consider objectionable before passing + them to the script. +

+

+ Servers MUST make this URI component available + to CGI scripts. The PATH_INFO + value is case-sensitive, and the + server MUST preserve the case of the PATH_INFO element of the URI + when making it available to scripts. +

+ +

+ + 6.1.7. PATH_TRANSLATED + +

+

+ PATH_TRANSLATED is derived by taking any path-info component of the + request URI (see + section 6.1.6), decoding it + (see section 3.1), parsing it as a URI in its own + right, and performing any virtual-to-physical + translation appropriate to map it onto the + server's document repository structure. + If the request URI includes no path-info + component, the PATH_TRANSLATED metavariable SHOULD NOT be defined. +

+

+
+    PATH_TRANSLATED = *CHAR
+  
+

+ For a request such as the following: +

+

+
+    http://somehost.com/cgi-bin/somescript/this%2eis%2epath%2einfo
+  
+

+ the PATH_INFO component would be decoded, and the result + parsed as though it were a request for the following: +

+

+
+    http://somehost.com/this.is.the.path.info
+  
+

+ This would then be translated to a + location in the server's document repository, + perhaps a filesystem path something + like this: +

+

+
+    /usr/local/www/htdocs/this.is.the.path.info
+  
+

+ The result of the translation is the value of PATH_TRANSLATED. +

+

+ The value of PATH_TRANSLATED may or may not map to a valid + repository + location. + Servers MUST preserve the case of the path-info + segment if and only if the underlying + repository + supports case-sensitive + names. If the + repository + is only case-aware, case-preserving, or case-blind + with regard to + document names, + servers are not required to preserve the + case of the original segment through the translation. +

+

+ The + translation + algorithm the server uses to derive PATH_TRANSLATED is + implementation defined; CGI scripts which use this variable may + suffer limited portability. +

+

+ Servers SHOULD provide this metavariable + to scripts if and only if the request URI includes a + path-info component. +

+ +

+ + 6.1.8. QUERY_STRING + +

+

+ A URL-encoded + string; the <query> part of the + Script-URI. + (See + section 3.2.) +

+

+
+    QUERY_STRING = query-string
+    query-string = *uric
+  
+

+ The URL syntax for a query + string is described in + section 3 of + RFC 2396 + [4]. +

+

+ Servers MUST supply this value to scripts. + The QUERY_STRING value is case-sensitive. + If the Script-URI does not include a query component, + the QUERY_STRING metavariable MUST be defined as an empty string (""). +

+ +

+ + 6.1.9. REMOTE_ADDR + +

+

+ The IP address of the client + sending the request to the server. This + is not necessarily that of the user + agent + (such as if the request came through a proxy). +

+

+
+    REMOTE_ADDR  = hostnumber
+    hostnumber   = ipv4-address | ipv6-address
+  
+

+ The definitions of ipv4-address and ipv6-address + are provided in Appendix B of RFC 2373 [13]. +

+

+ Servers MUST supply this value to scripts. +

+ +

+ + 6.1.10. REMOTE_HOST + +

+

+ The fully qualified domain name of the + client sending the request to + the server, if available, otherwise NULL. + (See section 6.1.9.) + Fully qualified domain names take the form as described in + section 3.5 of RFC 1034 [10] and section 2.1 of + RFC 1123 [5]. Domain names are not case sensitive. +

+

+ Servers SHOULD provide this information to + scripts. +

+ +

+ + 6.1.11. REMOTE_IDENT + +

+

+ The identity information reported about the connection by a + RFC 1413 [11] request to the remote agent, if + available. Servers + MAY choose not + to support this feature, or not to request the data + for efficiency reasons. +

+

+
+    REMOTE_IDENT = *CHAR
+  
+

+ The data returned + may be used for authentication purposes, but the level + of trust reposed in them should be minimal. +

+

+ Servers MAY supply this information to scripts if the + RFC1413 [11] lookup is performed. +

+ +

+ + 6.1.12. REMOTE_USER + +

+

+ If the request required authentication using the "Basic" + mechanism (i.e., the AUTH_TYPE + metavariable is set + to "Basic"), then the value of the REMOTE_USER + metavariable is set to the + user-ID supplied. In all other cases + the value of this metavariable + is undefined. +

+

+
+    REMOTE_USER = *OCTET
+  
+

+ This variable is specific to requests made via the + HTTP protocol. +

+

+ Servers SHOULD provide this metavariable + to scripts. +

+ +

+ + 6.1.13. REQUEST_METHOD + +

+

+ The REQUEST_METHOD + metavariable + is set to the + method with which the request was made, as described in section + 5.1.1 of the HTTP/1.0 specification [3] and + section 5.1.1 of the + HTTP/1.1 specification [8]. +

+

+
+    REQUEST_METHOD   = http-method
+    http-method      = "GET" | "HEAD" | "POST" | "PUT" | "DELETE"
+                       | "OPTIONS" | "TRACE" | extension-method
+    extension-method = token
+  
+

+ The method is case sensitive. + CGI/1.1 servers MAY choose to process some methods + directly rather than passing them to scripts. +

+

+ This variable is specific to requests made with HTTP. +

+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.14. SCRIPT_NAME + +

+

+ The SCRIPT_NAME + metavariable + is + set to a URL path that could identify the CGI script (rather than the + script's + output). The syntax and semantics are identical to a + decoded HTTP URL 'path' token + (see RFC 2396 + [4]). +

+

+
+    SCRIPT_NAME = "" | ( "/" [ path ] )
+  
+

+ The SCRIPT_NAME string is some leading part of the <path> component + of the Script-URI derived in some + implementation defined manner. + No PATH_INFO or QUERY_STRING segments + (see sections 6.1.6 and + 6.1.8) are included + in the SCRIPT_NAME value. +

+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.15. SERVER_NAME + +

+

+ The SERVER_NAME + metavariable + is set to the + name of the + server, as + derived from the <host> part of the + Script-URI + (see section 3.2). +

+

+
+    SERVER_NAME = hostname | hostnumber
+  
+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.16. SERVER_PORT + +

+

+ The SERVER_PORT + metavariable + is set to the + port on which the + request was received, as used in the <port> + part of the Script-URI. +

+

+
+    SERVER_PORT = 1*digit
+  
+

+ If the <port> portion of the script-URI is blank, the actual + port number upon which the request was received MUST be supplied. +

+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.17. SERVER_PROTOCOL + +

+

+ The SERVER_PROTOCOL + metavariable + is set to + the + name and revision of the information protocol with which + the + request + arrived. This is not necessarily the same as the protocol version used by + the server in its response to the client. +

+

+
+    SERVER_PROTOCOL   = HTTP-Version | extension-version
+                        | extension-token
+    HTTP-Version      = "HTTP" "/" 1*digit "." 1*digit
+    extension-version = protocol "/" 1*digit "." 1*digit
+    protocol          = 1*( alpha | digit | "+" | "-" | "." )
+    extension-token   = token
+  
+

+ 'protocol' is a version of the <scheme> part of the + Script-URI, but is + not identical to it. For example, the scheme of a request may be + "https" while the protocol remains "http". + The protocol is not case sensitive, but + by convention, 'protocol' is in + upper case. +

+

+ A well-known extension token value is "INCLUDED", + which signals that the current document is being included as part of + a composite document, rather than being the direct target of the + client request. +

+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.1.18. SERVER_SOFTWARE + +

+

+ The SERVER_SOFTWARE + metavariable + is set to the + name and version of the information server software answering the + request (and running the gateway). +

+

+
+    SERVER_SOFTWARE = 1*product
+    product         = token [ "/" product-version ]
+    product-version = token
+  
+

+ Servers MUST provide this metavariable + to scripts. +

+ +

+ + 6.2. Request Message-Bodies + +

+

+ As there may be a data entity attached to the request, there MUST be + a system defined method for the script to read + these data. Unless + defined otherwise, this will be via the 'standard input' file + descriptor. +

+

+ If the CONTENT_LENGTH value (see section 6.1.2) + is non-NULL, the server MUST supply at least that many bytes to + scripts on the standard input stream. + Scripts are + not obliged to read the data. + Servers MAY signal an EOF condition after CONTENT_LENGTH bytes have been + read, but are + not obligated to do so. Therefore, scripts + MUST NOT + attempt to read more than CONTENT_LENGTH bytes, even if more data + are available. +

+

+ For non-parsed header (NPH) scripts (see + section 7.1 + below), + servers SHOULD + attempt to ensure that the data + supplied to the script are precisely + as supplied by the client and unaltered by + the server. +

+

+ Section 8.1.2 describes the requirements of + servers with regard to requests that include + message-bodies. +

+ +

+ + 7. Data Output from the CGI Script + +

+

+ There MUST be a system defined method for the script to send data + back to the server or client; a script MUST always return some data. + Unless defined otherwise, this will be via the 'standard + output' file descriptor. +

+

+ There are two forms of output that scripts can supply to servers: non-parsed + header (NPH) output, and parsed header output. + Servers MUST support parsed header + output and MAY support NPH output. The method of + distinguishing between the two + types of output (or scripts) is implementation defined. +

+

+ Servers MAY implement a timeout period within which data must be + received from scripts. If a server implementation defines such + a timeout and receives no data from a script within the timeout + period, the server MAY terminate the script process and SHOULD + abort the client request with + either a + '504 Gateway Timed Out' or a + '500 Internal Server Error' response. +

+ +

+ + 7.1. Non-Parsed Header Output + +

+

+ Scripts using the NPH output form + MUST return a complete HTTP response message, as described + in Section 6 of the HTTP specifications + [3,8]. + NPH scripts + MUST use the SERVER_PROTOCOL variable to determine the appropriate format + for a response. +

+

+ Servers + SHOULD attempt to ensure that the script output is sent + directly to the client, with minimal + internal and no transport-visible + buffering. +

+ +

+ + 7.2. Parsed Header Output + +

+

+ Scripts using the parsed header output form MUST supply + a CGI response message to the server + as follows: +

+

+
+    CGI-Response   = *optional-field CGI-Field *optional-field NL [ Message-Body ]
+    optional-field = ( CGI-Field | HTTP-Field )
+    CGI-Field      = Content-type
+                   | Location
+                   | Status
+                   | extension-header
+  
+

+ The response comprises a header and a body, separated by a blank line. + The body may be NULL. + The header fields are either CGI header fields to be interpreted by + the server, or HTTP header fields + to be included in the response returned + to the client + if the request method is HTTP. At least one + CGI-Field MUST be + supplied, but no CGI field name may be used more than once + in a response. + If a body is supplied, then a "Content-type" + header field MUST be + supplied by the script, + otherwise the script MUST send a "Location" + or "Status" header field. If a + Location CGI-Field + is returned, then the script MUST NOT supply + any HTTP-Fields. +

+

+ Each header field in a CGI-Response MUST be specified on a single line; + CGI/1.1 does not support continuation lines. +

+ +

+ + 7.2.1. CGI header fields + +

+

+ The CGI header fields have the generic syntax: +

+

+
+    generic-field  = field-name ":" [ field-value ] NL
+    field-name     = token
+    field-value    = *( field-content | LWSP )
+    field-content  = *( token | tspecial | quoted-string )
+  
+

+ The field-name is not case sensitive; a NULL field value is + equivalent to the header field not being sent. +

+ +

+ + 7.2.1.1. Content-Type + +

+

+ The Internet Media Type [9] of the entity + body, which is to be sent unmodified to the client. +

+

+
+    Content-Type = "Content-Type" ":" media-type NL
+  
+

+ This is actually an HTTP-Field + rather than a CGI-Field, but + it is listed here because of its importance in the CGI dialogue as + a member of the "one of these is required" set of header + fields. +

+ +

+ + 7.2.1.2. Location + +

+

+ This is used to specify to the server that the script is returning a + reference to a document rather than an actual document. +

+

+
+    Location         = "Location" ":"
+                       ( fragment-URI | rel-URL-abs-path ) NL
+    fragment-URI     = URI [ # fragmentid ]
+    URI              = scheme ":" *qchar
+    fragmentid       = *qchar
+    rel-URL-abs-path = "/" [ hpath ] [ "?" query-string ]
+    hpath            = fpsegment *( "/" psegment )
+    fpsegment        = 1*hchar
+    psegment         = *hchar
+    hchar            = alpha | digit | safe | extra
+                       | ":" | "@" | "& | "="
+  
+

+ The Location + value is either an absolute URI with optional fragment, + as defined in RFC 1630 [1], or an absolute path + within the server's URI space (i.e., + omitting the scheme and network-related fields) and optional + query-string. If an absolute URI is returned by the script, + then the + server MUST generate a + '302 redirect' HTTP response + message unless the script has supplied an + explicit Status response header field. + Scripts returning an absolute URI MAY choose to + provide a message-body. Servers MUST make any appropriate modifications + to the script's output to ensure the response to the user-agent complies + with the response protocol version. + If the Location value is a path, then the server + MUST generate + the response that it would have produced in response to a request + containing the URL +

+

+
+    scheme "://" SERVER_NAME ":" SERVER_PORT rel-URL-abs-path
+  
+

+ Note: If the request was accompanied by a + message-body + (such as for a POST request), and the script + redirects the request with a Location field, the + message-body + may not be + available to the resource that is the target of the redirect. +

+ +

+ + 7.2.1.3. Status + +

+

+ The "Status" header field is used to indicate to the server what + status code the server MUST use in the response message. +

+

+
+    Status        = "Status" ":" digit digit digit SP reason-phrase NL
+    reason-phrase = *<CHAR, excluding CTLs, NL>
+  
+

+ The valid status codes are listed in section 6.1.1 of the HTTP/1.0 + specifications [3]. If the SERVER_PROTOCOL is + "HTTP/1.1", then the status codes defined in the HTTP/1.1 + specification [8] may + be used. If the script does not return a "Status" header + field, then "200 OK" SHOULD be assumed by the server. +

+

+ If a script is being used to handle a particular error or condition + encountered by the server, such as a '404 Not Found' error, the script + SHOULD use the "Status" CGI header field to propagate the error + condition back to the client. E.g., in the example mentioned it + SHOULD include a "Status: 404 Not Found" in the + header data returned to the server. +

+ +

+ + 7.2.1.4. Extension header fields + +

+

+ Scripts MAY include in their CGI response header additional fields + not defined in this or the HTTP specification. + These are called "extension" fields, + and have the syntax of a generic-field as defined in + section 7.2.1. The name of an extension field + MUST NOT conflict with a field name defined in this or any other + specification; extension field names SHOULD begin with "X-CGI-" + to ensure uniqueness. +

+ +

+ + 7.2.2. HTTP header fields + +

+

+ The script MAY return any other header fields defined by the + specification + for the SERVER_PROTOCOL (HTTP/1.0 [3] or HTTP/1.1 + [8]). + Servers MUST resolve conflicts beteen CGI header + and HTTP header formats or names (see section 8). +

+ +

+ + 8. Server Implementation + +

+

+ This section defines the requirements that must be met by HTTP + servers in order to provide a coherent and correct CGI/1.1 + environment in which scripts may function. It is intended + primarily for server implementors, but it is useful for + script authors to be familiar with the information as well. +

+ +

+ + 8.1. Requirements for Servers + +

+

+ In order to be considered CGI/1.1-compliant, a server must meet + certain basic criteria and provide certain minimal functionality. + The details of these requirements are described in the following sections. +

+ +

+ + 8.1.1. Script-URI + +

+

+ Servers MUST support the standard mechanism (described below) which + allows + script authors to determine + what URL to use in documents + which reference the script; + specifically, what URL to use in order to + achieve particular settings of the + metavariables. This + mechanism is as follows: +

+

+ The server + MUST translate the header data from the CGI header field syntax to + the HTTP + header field syntax if these differ. For example, the character + sequence for + newline (such as Unix's ASCII NL) used by CGI scripts may not be the + same as that used by HTTP (ASCII CR followed by LF). The server MUST + also resolve any conflicts between header fields returned by the script + and header fields that it would otherwise send itself. +

+ +

+ + 8.1.2. Request Message-body Handling + +

+

+ These are the requirements for server handling of message-bodies directed + to CGI/1.1 resources: +

+
    +
  1. The message-body the server provides to the CGI script MUST + have any transfer encodings removed. +
  2. +
  3. The server MUST derive and provide a value for the CONTENT_LENGTH + metavariable that reflects the length of the message-body after any + transfer decoding. +
  4. +
  5. The server MUST leave intact any content-encodings of the message-body. +
  6. +
+ +

+ + 8.1.3. Required Metavariables + +

+

+ Servers MUST provide scripts with certain information and + metavariables + as described in section 8.3. +

+ +

+ + 8.1.4. Response Compliance + +

+

+ Servers MUST ensure that responses sent to the user-agent meet all + requirements of the protocol level in effect. This may involve + modifying, deleting, or augmenting any header + fields and/or message-body supplied by the script. +

+ +

+ + 8.2. Recommendations for Servers + +

+

+ Servers SHOULD provide the "query" component of the script-URI + as command-line arguments to scripts if it does not + contain any unencoded '=' characters and the command-line arguments can + be generated in an unambiguous manner. + (See section 5.) +

+

+ Servers SHOULD set the AUTH_TYPE + metavariable to the value of the + 'auth-scheme' token of the "Authorization" + field if it was supplied as part of the request header. + (See section 6.1.1.) +

+

+ Where applicable, servers SHOULD set the current working directory + to the directory in which the script is located before invoking + it. +

+

+ Servers MAY reject with error '404 Not Found' + any requests that would result in + an encoded "/" being decoded into PATH_INFO or SCRIPT_NAME, as this + might represent a loss of information to the script. +

+

+ Although the server and the CGI script need not be consistent in + their handling of URL paths (client URLs and the PATH_INFO data, + respectively), server authors may wish to impose consistency. + So the server implementation SHOULD define its behaviour for the + following cases: +

+
    +
  1. define any restrictions on allowed characters, in particular + whether ASCII NUL is permitted; +
  2. +
  3. define any restrictions on allowed path segments, in particular + whether non-terminal NULL segments are permitted; +
  4. +
  5. define the behaviour for "." or ".." path + segments; i.e., whether they are prohibited, treated as + ordinary path + segments or interpreted in accordance with the relative URL + specification [7]; +
  6. +
  7. define any limits of the implementation, including limits on path or + search string lengths, and limits on the volume of header data the server + will parse. +
  8. +
+

+ Servers MAY generate the + Script-URI in + any way from the client URI, + or from any other data (but the behaviour SHOULD be documented). +

+

+ For non-parsed header (NPH) scripts (see + section 7.1), servers SHOULD + attempt to ensure that the script input comes directly from the + client, with minimal buffering. For all scripts the data will be + as supplied by the client. +

+ +

+ + 8.3. Summary of + MetaVariables + +

+

+ Servers MUST provide the following + metavariables to + scripts. See the individual descriptions for exceptions and semantics. +

+

+
+    CONTENT_LENGTH (section 6.1.2)
+    CONTENT_TYPE (section 6.1.3)
+    GATEWAY_INTERFACE (section 6.1.4)
+    PATH_INFO (section 6.1.6)
+    QUERY_STRING (section 6.1.8)
+    REMOTE_ADDR (section 6.1.9)
+    REQUEST_METHOD (section 6.1.13)
+    SCRIPT_NAME (section 6.1.14)
+    SERVER_NAME (section 6.1.15)
+    SERVER_PORT (section 6.1.16)
+    SERVER_PROTOCOL (section 6.1.17)
+    SERVER_SOFTWARE (section 6.1.18)
+  
+

+ Servers SHOULD define the following + metavariables for scripts. + See the individual descriptions for exceptions and semantics. +

+

+
+    AUTH_TYPE (section 6.1.1)
+    REMOTE_HOST (section 6.1.10)
+  
+

+ In addition, servers SHOULD provide + metavariables for all fields present + in the HTTP request header, with the exception of those involved with + access control. Servers MAY at their discretion provide + metavariables + for access control fields. +

+

+ Servers MAY define the following + metavariables. See the individual + descriptions for exceptions and semantics. +

+

+
+    PATH_TRANSLATED (section 6.1.7)
+    REMOTE_IDENT (section 6.1.11)
+    REMOTE_USER (section 6.1.12)
+  
+

+ Servers MAY + at their discretion define additional implementation-specific + extension metavariables + provided their names do not + conflict with defined header field names. Implementation-specific + metavariable names SHOULD + be prefixed with "X_" (e.g., + "X_DBA") to avoid the potential for such conflicts. +

+ +

+ + 9. + Script Implementation + +

+

+ This section defines the requirements and recommendations for scripts + that are intended to function in a CGI/1.1 environment. It is intended + primarily as a reference for script authors, but server implementors + should be familiar with these issues as well. +

+ +

+ + 9.1. Requirements for Scripts + +

+

+ Scripts using the parsed-header method to communicate with servers + MUST supply a response header to the server. + (See section 7.) +

+

+ Scripts using the NPH method to communicate with servers MUST + provide complete HTTP responses, and MUST use the value of the + SERVER_PROTOCOL metavariable + to determine the appropriate format. + (See section 7.1.) +

+

+ Scripts MUST check the value of the REQUEST_METHOD + metavariable in order + to provide an appropriate response. + (See section 6.1.13.) +

+

+ Scripts MUST be prepared to handled URL-encoded values in + metavariables. + In addition, they MUST recognise both "+" and "%20" in URL-encoded + quantities as representing the space character. + (See section 3.1.) +

+

+ Scripts MUST ignore leading zeros in the major and minor version numbers + in the GATEWAY_INTERFACE + metavariable value. (See + section 6.1.4.) +

+

+ When processing requests that include a + message-body, scripts + MUST NOT read more than CONTENT_LENGTH bytes from the input stream. + (See sections 6.1.2 and 6.2.) +

+ +

+ + 9.2. Recommendations for Scripts + +

+

+ Servers may interrupt or terminate script execution at any time + and without warning, so scripts SHOULD be prepared to deal with + abnormal termination. +

+

+ Scripts MUST + reject with + error '405 Method Not + Allowed' requests + made using methods that they do not support. If the script does + not intend + processing the PATH_INFO data, then it SHOULD reject the request with + '404 Not + Found' if PATH_INFO is not NULL. +

+

+ If a script is processing the output of a form, it SHOULD + verify that the CONTENT_TYPE + is "application/x-www-form-urlencoded" [2] + or whatever other media type is expected. +

+

+ Scripts parsing PATH_INFO, + PATH_TRANSLATED, or SCRIPT_NAME + SHOULD be careful + of void path segments ("//") and special path segments + ("." and + ".."). They SHOULD either be removed from the path before + use in OS + system calls, or the request SHOULD be rejected with + '404 Not Found'. +

+

+ As it is impossible for + scripts to determine the client URI that + initiated a + request without knowledge of the specific server in + use, the script SHOULD NOT return "text/html" + documents containing + relative URL links without including a "<BASE>" + tag in the document. +

+

+ When returning header fields, + scripts SHOULD try to send the CGI + header fields (see section + 7.2) as soon as possible, and + SHOULD send them + before any HTTP header fields. This may + help reduce the server's memory requirements. +

+ +

+ + 10. System Specifications + +

+ +

+ + 10.1. AmigaDOS + +

+

+ The implementation of the CGI on an AmigaDOS operating system platform + SHOULD use environment variables as the mechanism of providing + request metadata to CGI scripts. +

+
+
Environment variables +
+
+

+ These are accessed by the DOS library routine GetVar. The + flags argument SHOULD be 0. Case is ignored, but upper case is + recommended for compatibility with case-sensitive systems. +

+
+
The current working directory +
+
+

+ The current working directory for the script is set to the directory + containing the script. +

+
+
Character set +
+
+

+ The US-ASCII character set is used for the definition of environment + variable names and header + field names; the newline (NL) sequence is LF; + servers SHOULD also accept CR LF as a newline. +

+
+
+ +

+ + 10.2. Unix + +

+

+ The implementation of the CGI on a UNIX operating system platform + SHOULD use environment variables as the mechanism of providing + request metadata to CGI scripts. +

+

+ For Unix compatible operating systems, the following are defined: +

+
+
Environment variables +
+
+

+ These are accessed by the C library routine getenv. +

+
+
The command line +
+
+

+ This is accessed using the + argc and argv + arguments to main(). The words have any characters + that + are 'active' in the Bourne shell escaped with a backslash. + If the value of the QUERY_STRING + metavariable + contains an unencoded equals-sign '=', then the command line + SHOULD NOT be used by the script. +

+
+
The current working directory +
+
+

+ The current working directory for the script + SHOULD be set to the directory + containing the script. +

+
+
Character set +
+
+

+ The US-ASCII character set is used for the definition of environment + variable names and header field names; the newline (NL) sequence is LF; + servers SHOULD also accept CR LF as a newline. +

+
+
+ +

+ + 11. Security Considerations + +

+ +

+ + 11.1. Safe Methods + +

+

+ As discussed in the security considerations of the HTTP + specifications [3,8], the + convention has been established that the + GET and HEAD methods should be 'safe'; they should cause no + side-effects and only have the significance of resource retrieval. +

+

+ CGI scripts are responsible for enforcing any HTTP security considerations + [3,8] + with respect to the protocol version level of the request and + any side effects generated by the scripts on behalf of + the server. Primary + among these + are the considerations of safe and idempotent methods. Idempotent + requests are those that may be repeated an arbitrary number of times + and produce side effects identical to a single request. +

+ +

+ + 11.2. HTTP Header + Fields Containing Sensitive Information + +

+

+ Some HTTP header fields may carry sensitive information which the server + SHOULD NOT pass on to the script unless explicitly configured to do + so. For example, if the server protects the script using the + "Basic" + authentication scheme, then the client will send an + "Authorization" + header field containing a username and password. If the server, rather + than the script, validates this information then the password SHOULD + NOT be passed on to the script via the HTTP_AUTHORIZATION + metavariable + without careful consideration. + This also applies to the + Proxy-Authorization header field and the corresponding + HTTP_PROXY_AUTHORIZATION + metavariable. +

+ +

+ + 11.3. Script + Interference with the Server + +

+

+ The most common implementation of CGI invokes the script as a child + process using the same user and group as the server process. It + SHOULD therefore be ensured that the script cannot interfere with the + server process, its configuration, or documents. +

+

+ If the script is executed by calling a function linked in to the + server software (either at compile-time or run-time) then precautions + SHOULD be taken to protect the core memory of the server, or to + ensure that untrusted code cannot be executed. +

+ +

+ + 11.4. Data Length and Buffering Considerations + +

+

+ This specification places no limits on the length of message-bodies + presented to the script. Scripts should not assume that statically + allocated buffers of any size are sufficient to contain the entire + submission at one time. Use of a fixed length buffer without careful + overflow checking may result in an attacker exploiting 'stack-smashing' + or 'stack-overflow' vulnerabilities of the operating system. + Scripts may spool large submissions to disk or other buffering media, + but a rapid succession of large submissions may result in denial of + service conditions. If the CONTENT_LENGTH of a message-body is larger + than resource considerations allow, scripts should respond with an + error status appropriate for the protocol version; potentially applicable + status codes include '503 Service Unavailable' (HTTP/1.0 and HTTP/1.1), + '413 Request Entity Too Large' (HTTP/1.1), and + '414 Request-URI Too Long' (HTTP/1.1). +

+ +

+ + 11.5. Stateless Processing + +

+

+ The stateless nature of the Web makes each script execution and resource + retrieval independent of all others even when multiple requests constitute a + single conceptual Web transaction. Because of this, a script should not + make any assumptions about the context of the user-agent submitting a + request. In particular, scripts should examine data obtained from the client + and verify that they are valid, both in form and content, before allowing + them to be used for sensitive purposes such as input to other + applications, commands, or operating system services. These uses + include, but are not + limited to: system call arguments, database writes, dynamically evaluated + source code, and input to billing or other secure processes. It is important + that applications be protected from invalid input regardless of whether + the invalidity is the result of user error, logic error, or malicious action. +

+

+ Authors of scripts involved in multi-request transactions should be + particularly cautios about validating the state information; + undesirable effects may result from the substitution of dangerous + values for portions of the submission which might otherwise be + presumed safe. Subversion of this type occurs when alterations + are made to data from a prior stage of the transaction that were + not meant to be controlled by the client (e.g., hidden + HTML form elements, cookies, embedded URLs, etc.). +

+ +

+ + 12. Acknowledgements + +

+

+ This work is based on a draft published in 1997 by David R. Robinson, + which in turn was based on the original CGI interface that arose out of + discussions on the www-talk mailing list. In particular, + Rob McCool, John Franks, Ari Luotonen, + George Phillips and + Tony Sanders deserve special recognition for their efforts in + defining and implementing the early versions of this interface. +

+

+ This document has also greatly benefited from the comments and + suggestions made by Chris Adie, Dave Kristol, + Mike Meyer, David Morris, Jeremy Madea, + Patrick McManus, Adam Donahue, + Ross Patterson, and Harald Alvestrand. +

+ +

+ + 13. References + +

+
+
[1] +
+
Berners-Lee, T., 'Universal Resource Identifiers in WWW: A + Unifying Syntax for the Expression of Names and Addresses of + Objects on the Network as used in the World-Wide Web', RFC 1630, + CERN, June 1994. +

+

+
+
[2] +
+
Berners-Lee, T. and Connolly, D., 'Hypertext Markup Language - + 2.0', RFC 1866, MIT/W3C, November 1995. +

+

+
+
[3] +
+
Berners-Lee, T., Fielding, R. T. and Frystyk, H., + 'Hypertext Transfer Protocol -- HTTP/1.0', RFC 1945, MIT/LCS, + UC Irvine, May 1996. +

+

+
+ +
[4] +
+
Berners-Lee, T., Fielding, R., and Masinter, L., Editors, + 'Uniform Resource Identifiers (URI): Generic Syntax', RFC 2396, + MIT, U.C. Irvine, Xerox Corporation, August 1996. +

+

+
+ +
[5] +
+
Braden, R., Editor, 'Requirements for Internet Hosts -- + Application and Support', STD 3, RFC 1123, IETF, October 1989. +

+

+
+
[6] +
+
Crocker, D.H., 'Standard for the Format of ARPA Internet Text + Messages', STD 11, RFC 822, University of Delaware, August 1982. +

+

+
+
[7] +
+
Fielding, R., 'Relative Uniform Resource Locators', RFC 1808, + UC Irvine, June 1995. +

+

+
+
[8] +
+
Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and + Berners-Lee, T., 'Hypertext Transfer Protocol -- HTTP/1.1', + RFC 2068, UC Irvine, DEC, + MIT/LCS, January 1997. +

+

+
+
[9] +
+
Freed, N. and Borenstein N., 'Multipurpose Internet Mail + Extensions (MIME) Part Two: Media Types', RFC 2046, Innosoft, + First Virtual, November 1996. +

+

+
+
[10] +
+
Mockapetris, P., 'Domain Names - Concepts and Facilities', + STD 13, RFC 1034, ISI, November 1987. +

+

+
+
[11] +
+
St. Johns, M., 'Identification Protocol', RFC 1431, US + Department of Defense, February 1993. +

+

+
+
[12] +
+
'Coded Character Set -- 7-bit American Standard Code for + Information Interchange', ANSI X3.4-1986. +

+

+
+
[13] +
+
Hinden, R. and Deering, S., + 'IP Version 6 Addressing Architecture', RFC 2373, + Nokia, Cisco Systems, + July 1998. +

+

+
+
+ +

+ + 14. Authors' Addresses + +

+
+

+ Ken A L Coar +
+ MeepZor Consulting +
+ 7824 Mayfaire Crest Lane, Suite 202 +
+ Raleigh, NC 27615-4875 +
+ U.S.A. +

+

+ Tel: +1 (919) 254.4237 +
+ Fax: +1 (919) 254.5250 +
+ Email: + Ken.Coar@Golux.Com +

+
+
+

+ David Robinson +
+ E*TRADE UK Ltd +
+ Mount Pleasant House +
+ 2 Mount Pleasant +
+ Huntingdon Road +
+ Cambridge CB3 0RN +
+ UK +

+

+ Tel: +44 (1223) 566926 +
+ Fax: +44 (1223) 506288 +
+ Email: + drtr@etrade.co.uk +

+ + + diff --git a/busybox/docs/ifupdown_design.txt b/busybox/docs/ifupdown_design.txt new file mode 100644 index 00000000000000..39e28a9f455fb2 --- /dev/null +++ b/busybox/docs/ifupdown_design.txt @@ -0,0 +1,44 @@ +This document is meant to convince you to not use ifup/ifdown. + + +The general problem with ifupdown is that it is "copulated in vertical +fashion" by design. It tries to do the job of shell script in C, +and this is invariably doomed to fail. You need ifup/ifdown +to be adaptable by local admins, and C is an extremely poor choice +for that. + +We are doomed to have problems with ifup/ifdown. Just look as this code: + +static const struct dhcp_client_t ext_dhcp_clients[] = { + { "dhcpcd", "", "" }, + { "dhclient", ........ }, + { "pump", ........ }, + { "udhcpc", ........ }, +}; + +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 (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"); + return static_down(ifd, exec); +#elif ENABLE_UDHCPC + return execute("kill " + "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); +#else + return 0; /* no dhcp support */ +#endif +} + +How the hell it is supposed to work reliably this way? Just imagine that +admin is using pump and ifup/ifdown. It works. Then, for whatever reason, +admin installs dhclient, but does NOT use it. ifdown will STOP WORKING, +just because it will see installed dhclient binary in e.g. /usr/bin/dhclient! +This is stupid. + +I seriously urge people to not use ifup/ifdown. +Use something less brain damaged. diff --git a/busybox/docs/keep_data_small.txt b/busybox/docs/keep_data_small.txt new file mode 100644 index 00000000000000..218d4f2eeb6bba --- /dev/null +++ b/busybox/docs/keep_data_small.txt @@ -0,0 +1,265 @@ + Keeping data small + +When many applets are compiled into busybox, all rw data and +bss for each applet are concatenated. Including those from libc, +if static busybox is built. When busybox is started, _all_ this data +is allocated, not just that one part for selected applet. + +What "allocated" exactly means, depends on arch. +On NOMMU it's probably bites the most, actually using real +RAM for rwdata and bss. On i386, bss is lazily allocated +by COWed zero pages. Not sure about rwdata - also COW? + +In order to keep busybox NOMMU and small-mem systems friendly +we should avoid large global data in our applets, and should +minimize usage of libc functions which implicitly use +such structures. + +Small experiment to measure "parasitic" bbox memory consumption: +here we start 1000 "busybox sleep 10" in parallel. +busybox binary is practically allyesconfig static one, +built against uclibc. Run on x86-64 machine with 64-bit kernel: + +bash-3.2# nmeter '%t %c %m %p %[pn]' +23:17:28 .......... 168M 0 147 +23:17:29 .......... 168M 0 147 +23:17:30 U......... 168M 1 147 +23:17:31 SU........ 181M 244 391 +23:17:32 SSSSUUU... 223M 757 1147 +23:17:33 UUU....... 223M 0 1147 +23:17:34 U......... 223M 1 1147 +23:17:35 .......... 223M 0 1147 +23:17:36 .......... 223M 0 1147 +23:17:37 S......... 223M 0 1147 +23:17:38 .......... 223M 1 1147 +23:17:39 .......... 223M 0 1147 +23:17:40 .......... 223M 0 1147 +23:17:41 .......... 210M 0 906 +23:17:42 .......... 168M 1 147 +23:17:43 .......... 168M 0 147 + +This requires 55M of memory. Thus 1 trivial busybox applet +takes 55k of memory on 64-bit x86 kernel. + +On 32-bit kernel we need ~26k per applet. + +Script: + +i=1000; while test $i != 0; do + echo -n . + busybox sleep 30 & + i=$((i - 1)) +done +echo +wait + +(Data from NOMMU arches are sought. Provide 'size busybox' output too) + + + Example 1 + +One example how to reduce global data usage is in +archival/libarchive/decompress_gunzip.c: + +/* This is somewhat complex-looking arrangement, but it allows + * to place decompressor state either in bss or in + * malloc'ed space simply by changing #defines below. + * Sizes on i386: + * text data bss dec hex + * 5256 0 108 5364 14f4 - bss + * 4915 0 0 4915 1333 - malloc + */ +#define STATE_IN_BSS 0 +#define STATE_IN_MALLOC 1 + +(see the rest of the file to get the idea) + +This example completely eliminates globals in that module. +Required memory is allocated in unpack_gz_stream() [its main module] +and then passed down to all subroutines which need to access 'globals' +as a parameter. + + + Example 2 + +In case you don't want to pass this additional parameter everywhere, +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/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: + +struct globals { int a; char buf[1000]; }; + +and then declare that ptr_to_globals is a pointer to it: + +#define G (*ptr_to_globals) + +ptr_to_globals is declared as constant pointer. +This helps gcc understand that it won't change, resulting in noticeably +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 _main(). Another variation is +to use stack: + +int _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. + + + bb_common_bufsiz1 + +There is one big common buffer in bss - bb_common_bufsiz1. It is a much +earlier mechanism to reduce bss usage. Each applet can use it for +its needs. Library functions are prohibited from using it. + +'G.' trick can be done using bb_common_bufsiz1 instead of malloced buffer: + +#define G (*(struct globals*)&bb_common_bufsiz1) + +Be careful, though, and use it only if globals fit into bb_common_bufsiz1. +Since bb_common_bufsiz1 is BUFSIZ + 1 bytes long and BUFSIZ can change +from one libc to another, you have to add compile-time check for it: + +if (sizeof(struct globals) > sizeof(bb_common_bufsiz1)) + BUG__globals_too_big(); + + + Drawbacks + +You have to initialize it by hand. xzalloc() can be helpful in clearing +allocated storage to 0, but anything more must be done by hand. + +All global variables are prefixed by 'G.' now. If this makes code +less readable, use #defines: + +#define dev_fd (G.dev_fd) +#define sector (G.sector) + + + Finding non-shared duplicated strings + +strings busybox | sort | uniq -c | sort -nr + + + gcc's data alignment problem + +The following attribute added in vi.c: + +static int tabstop; +static struct termios term_orig __attribute__ ((aligned (4))); +static struct termios term_vi __attribute__ ((aligned (4))); + +reduces bss size by 32 bytes, because gcc sometimes aligns structures to +ridiculously large values. asm output diff for above example: + + tabstop: + .zero 4 + .section .bss.term_orig,"aw",@nobits +- .align 32 ++ .align 4 + .type term_orig, @object + .size term_orig, 60 + term_orig: + .zero 60 + .section .bss.term_vi,"aw",@nobits +- .align 32 ++ .align 4 + .type term_vi, @object + .size term_vi, 60 + +gcc doesn't seem to have options for altering this behaviour. + +gcc 3.4.3 and 4.1.1 tested: +char c = 1; +// gcc aligns to 32 bytes if sizeof(struct) >= 32 +struct { + int a,b,c,d; + int i1,i2,i3; +} s28 = { 1 }; // struct will be aligned to 4 bytes +struct { + int a,b,c,d; + int i1,i2,i3,i4; +} s32 = { 1 }; // struct will be aligned to 32 bytes +// same for arrays +char vc31[31] = { 1 }; // unaligned +char vc32[32] = { 1 }; // aligned to 32 bytes + +-fpack-struct=1 reduces alignment of s28 to 1 (but probably +will break layout of many libc structs) but s32 and vc32 +are still aligned to 32 bytes. + +I will try to cook up a patch to add a gcc option for disabling it. +Meanwhile, this is where it can be disabled in gcc source: + +gcc/config/i386/i386.c +int +ix86_data_alignment (tree type, int align) +{ +#if 0 + if (AGGREGATE_TYPE_P (type) + && TYPE_SIZE (type) + && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST + && (TREE_INT_CST_LOW (TYPE_SIZE (type)) >= 256 + || TREE_INT_CST_HIGH (TYPE_SIZE (type))) && align < 256) + return 256; +#endif + +Result (non-static busybox built against glibc): + +# size /usr/srcdevel/bbox/fix/busybox.t0/busybox busybox + text data bss dec hex filename + 634416 2736 23856 661008 a1610 busybox + 632580 2672 22944 658196 a0b14 busybox_noalign + + + + Keeping code small + +Use scripts/bloat-o-meter to check whether introduced changes +didn't generate unnecessary bloat. This script needs unstripped binaries +to generate a detailed report. To automate this, just use +"make bloatcheck". It requires busybox_old binary to be present, +use "make baseline" to generate it from unmodified source, or +copy busybox_unstripped to busybox_old before modifying sources +and rebuilding. + +Set CONFIG_EXTRA_CFLAGS="-fno-inline-functions-called-once", +produce "make bloatcheck", see the biggest auto-inlined functions. +Now, set CONFIG_EXTRA_CFLAGS back to "", but add NOINLINE +to some of these functions. In 1.16.x timeframe, the results were +(annotated "make bloatcheck" output): + +function old new delta +expand_vars_to_list - 1712 +1712 win +lzo1x_optimize - 1429 +1429 win +arith_apply - 1326 +1326 win +read_interfaces - 1163 +1163 loss, leave w/o NOINLINE +logdir_open - 1148 +1148 win +check_deps - 1148 +1148 loss +rewrite - 1039 +1039 win +run_pipe 358 1396 +1038 win +write_status_file - 1029 +1029 almost the same, leave w/o NOINLINE +dump_identity - 987 +987 win +mainQSort3 - 921 +921 win +parse_one_line - 916 +916 loss +summarize - 897 +897 almost the same +do_shm - 884 +884 win +cpio_o - 863 +863 win +subCommand - 841 +841 loss +receive - 834 +834 loss + +855 bytes saved in total. + +scripts/mkdiff_obj_bloat may be useful to automate this process: run +"scripts/mkdiff_obj_bloat NORMALLY_BUILT_TREE FORCED_NOINLINE_TREE" +and select modules which shrank. diff --git a/busybox/docs/logging_and_backgrounding.txt b/busybox/docs/logging_and_backgrounding.txt new file mode 100644 index 00000000000000..c76cd36532c233 --- /dev/null +++ b/busybox/docs/logging_and_backgrounding.txt @@ -0,0 +1,98 @@ + Logging and backgrounding + +By default, bb_[p]error_msg[_and_die] messages go to stderr, +and of course, usually applets do not auto-background. :) + +Historically, daemons and inetd services are different. + +Busybox is trying to provide compatible behavior, thus if an applet +is emulating an existing utility, it should mimic it. If utility +auto-backgrounds itself, busybox applet should do the same. +If utility normally logs to syslog, busybox applet should do +the same too. + +However, busybox should not needlessly restrict the freedom +of the users. And users have different needs and different preferences. +Some might like logging everything from daemons to syslog. +Others prefer running stuff under runsv/svlogd and thus would like +logging to stderr and no daemonization. + +To help with that, busybox applets should have options to override +default behavior, whatever that is for a given applet. + + +Current situation is a bit of a mess: + +acpid - auto-backgrounds unless -d +crond - auto-backgrounds unless -f, logs to syslog unless -d or -L. + option -d logs to stderr, -L FILE logs to FILE +devfsd - (obsolete) +dnsd - option -d makes it background and log to syslog +fakeidentd - inetd service. Auto-backgrounds and logs to syslog + if no -f and no -i and no -w (-i is "inetd service" flag, + -w is "inetd-wait service" flag) +ftpd - inetd service. Logs to syslog with -S, with -v logs to strerr too +httpd - auto-backgrounds unless -f or -i (-i is "inetd service" flag) +inetd - auto-backgrounds unless -f, logs to syslog unless -e +klogd - auto-backgrounds unless -n +syslogd - auto-backgrounds unless -n +telnetd - auto-backgrounds unless -f or -i (-i is "inetd service" flag) +udhcpc - auto-backgrounds unless -f after lease is obtained, + option -b makes it background sooner (when lease attempt + fails and retries start), + after backgrounding it stops logging to stderr; + logs to stderr, but option -S makes it log *also* to syslog +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), + 5 never log to syslog (acpid httpd telnetd klogd syslogd, last two + - for obviously correct reasons), + there are no daemons which always log to syslog, + 12 auto-background if not run as inetd services (all except dnsd. + Note that there is no "standard" dnsd AFAIKS). But see below + for daemons (tcpsvd etc) which don't auto-background. + +miscutils/crond.c: logmode = LOGMODE_SYSLOG; +networking/dnsd.c: logmode = LOGMODE_SYSLOG; +networking/ftpd.c: logmode = LOGMODE_NONE; +networking/ftpd.c: logmode |= LOGMODE_SYSLOG; +networking/inetd.c: logmode = LOGMODE_SYSLOG; +networking/isrv_identd.c: logmode = LOGMODE_SYSLOG; +networking/telnetd.c: logmode = LOGMODE_SYSLOG; +networking/udhcp/dhcpc.c: logmode = LOGMODE_NONE; +networking/udhcp/dhcpc.c: logmode |= LOGMODE_SYSLOG; +networking/udhcp/dhcpc.c: logmode &= ~LOGMODE_STDIO; +networking/udhcp/dhcpd.c: logmode = LOGMODE_NONE; +networking/udhcp/dhcpd.c: logmode |= LOGMODE_SYSLOG; +networking/zcip.c: logmode |= LOGMODE_SYSLOG; + + +These daemons never auto-background and never log to syslog: + +lpd - inetd service. Has nothing to log so far, though +dhcprelay - standard behavior +inotifyd - standard behavior +runsv - standard behavior +runsvdir - standard behavior +svlogd - standard behavior +tcpsvd, udpsvd - standard behavior +tftpd - standard behavior + + +Non-daemons (seems to be use syslog for a good reason): + +networking/nameif.c: logmode |= LOGMODE_SYSLOG; +loginutils/chpasswd.c: logmode = LOGMODE_BOTH; +loginutils/chpasswd.c: logmode = LOGMODE_STDIO; +loginutils/getty.c: logmode = LOGMODE_BOTH; +loginutils/getty.c: logmode = LOGMODE_NONE; +loginutils/passwd.c: logmode = LOGMODE_STDIO; +loginutils/passwd.c: logmode = LOGMODE_BOTH; +loginutils/sulogin.c: logmode = LOGMODE_SYSLOG; (used if stdio isn't a tty) +loginutils/sulogin.c: logmode = LOGMODE_BOTH; +util-linux/mount.c: logmode = LOGMODE_SYSLOG; (used in a backgrounded NFS mount helper) diff --git a/busybox/docs/mdev.txt b/busybox/docs/mdev.txt new file mode 100644 index 00000000000000..b24025f7b73587 --- /dev/null +++ b/busybox/docs/mdev.txt @@ -0,0 +1,151 @@ +------------- + MDEV Primer +------------- + +For those of us who know how to use mdev, a primer might seem lame. For +everyone else, mdev is a weird black box that they hear is awesome, but can't +seem to get their head around how it works. Thus, a primer. + +----------- + Basic Use +----------- + +Mdev has two primary uses: initial population and dynamic updates. Both +require sysfs support in the kernel and have it mounted at /sys. For dynamic +updates, you also need to have hotplugging enabled in your kernel. + +Here's a typical code snippet from the init script: +[0] mount -t proc proc /proc +[1] mount -t sysfs sysfs /sys +[2] echo /sbin/mdev > /proc/sys/kernel/hotplug +[3] mdev -s + +Alternatively, without procfs the above becomes: +[1] mount -t sysfs sysfs /sys +[2] sysctl -w kernel.hotplug=/sbin/mdev +[3] mdev -s + + +Of course, a more "full" setup would entail executing this before the previous +code snippet: +[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev +[5] mkdir /dev/pts +[6] mount -t devpts devpts /dev/pts + +The simple explanation here is that [1] you need to have /sys mounted before +executing mdev. Then you [2] instruct the kernel to execute /sbin/mdev whenever +a device is added or removed so that the device node can be created or +destroyed. Then you [3] seed /dev with all the device nodes that were created +while the system was booting. + +For the "full" setup, you want to [4] make sure /dev is a tmpfs filesystem +(assuming you're running out of flash). Then you want to [5] create the +/dev/pts mount point and finally [6] mount the devpts filesystem on it. + +------------- + MDEV Config (/etc/mdev.conf) +------------- + +Mdev has an optional config file for controlling ownership/permissions of +device nodes if your system needs something more than the default root/root +660 permissions. + +The file has the format: + [-][envmatch] : +or + [envmatch]@ : +or + $envvar= : + +For example: + hd[a-z][0-9]* 0:3 660 + +The config file parsing stops at the first matching line. If no line is +matched, then the default of 0:0 660 is used. To set your own default, simply +create your own total match like so: + + .* 1:1 777 + +You can rename/move device nodes by using the next optional field. + + : [=path] + +So if you want to place the device node into a subdirectory, make sure the path +has a trailing /. If you want to rename the device node, just place the name. + hda 0:3 660 =drives/ +This will move "hda" into the drives/ subdirectory. + hdb 0:3 660 =cdrom +This will rename "hdb" to "cdrom". + +Similarly, ">path" renames/moves the device but it also creates +a direct symlink /dev/DEVNAME to the renamed/moved device. + +You can also prevent creation of device nodes with the 4th field as "!": + tty[a-z]. 0:0 660 ! + pty[a-z]. 0:0 660 ! + +If you also enable support for executing your own commands, then the file has +the format: + : [=path] [@|$|*] + or + : [>path] [@|$|*] + or + : [!] [@|$|*] + +For example: +---8<--- +# block devices +([hs]d[a-z]) root:disk 660 >disk/%1/0 +([hs]d[a-z])([0-9]+) root:disk 660 >disk/%1/%2 +mmcblk([0-9]+) root:disk 660 >disk/mmc/%1/0 +mmcblk([0-9]+)p([0-9]+) root:disk 660 >disk/mmc/%1/%2 +# network devices +(tun|tap) root:network 660 >net/%1 +---8<--- + +The special characters have the meaning: + @ Run after creating the device. + $ Run before removing the device. + * Run both after creating and before removing the device. + +The command is executed via the system() function (which means you're giving a +command to the shell), so make sure you have a shell installed at /bin/sh. You +should also keep in mind that the kernel executes hotplug helpers with stdin, +stdout, and stderr connected to /dev/null. + +For your convenience, the shell env var $MDEV is set to the device name. So if +the device "hdc" was matched, MDEV would be set to "hdc". + +---------- + FIRMWARE +---------- + +Some kernel device drivers need to request firmware at runtime in order to +properly initialize a device. Place all such firmware files into the +/lib/firmware/ directory. At runtime, the kernel will invoke mdev with the +filename of the firmware which mdev will load out of /lib/firmware/ and into +the kernel via the sysfs interface. The exact filename is hardcoded in the +kernel, so look there if you need to know how to name the file in userspace. + +------------ + SEQUENCING +------------ + +Kernel does not serialize hotplug events. It increments SEQNUM environmental +variable for each successive hotplug invocation. Normally, mdev doesn't care. +This may reorder hotplug and hot-unplug events, with typical symptoms of +device nodes sometimes not created as expected. + +However, if /dev/mdev.seq file is found, mdev will compare its +contents with SEQNUM. It will retry up to two seconds, waiting for them +to match. If they match exactly (not even trailing '\n' is allowed), +or if two seconds pass, mdev runs as usual, then it rewrites /dev/mdev.seq +with SEQNUM+1. + +IOW: this will serialize concurrent mdev invocations. + +If you want to activate this feature, execute "echo >/dev/mdev.seq" prior to +setting mdev to be the hotplug handler. This writes single '\n' to the file. +NB: mdev recognizes /dev/mdev.seq consisting of single '\n' character +as a special case. IOW: this will not make your first hotplug event +to stall for two seconds. diff --git a/busybox/docs/new-applet-HOWTO.txt b/busybox/docs/new-applet-HOWTO.txt new file mode 100644 index 00000000000000..619d47fb8fb67c --- /dev/null +++ b/busybox/docs/new-applet-HOWTO.txt @@ -0,0 +1,207 @@ +How to Add a New Applet to BusyBox +================================== + +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 + +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. + +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 and Config.in/Kbuild/usage/applet.h snippets (more on that below +in this document). Be sure to name the main function _main instead +of main. And be sure to put it in .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: + +(libbb.h already includes most usual header files. You do not need +#include etc...) + + +----begin example code------ + +/* vi: set sw=4 ts=4: */ +/* + * Mini mu implementation for busybox + * + * Copyright (C) [YEAR] by [YOUR NAME] + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +#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) +{ + int fd; + ssize_t n; + char mu; + + fd = xopen("/dev/random", O_RDONLY); + + if ((n = safe_read(fd, &mu, 1)) < 1) + bb_perror_msg_and_die("/dev/random"); + + return mu; +} + +----end example code------ + + +Coding Style +------------ + +Before you submit your applet for inclusion in BusyBox, (or better yet, before +you _write_ your applet) please read through the style guide in the docs +directory and make your program compliant. + + +Some Words on libbb +------------------- + +As you are writing your applet, please be aware of the body of pre-existing +useful functions in libbb. Use these instead of reinventing the wheel. + +Additionally, if you have any useful, general-purpose functions in your +applet that could be useful in other applets, consider putting them in libbb. + +And it may be possible that some of the other applets uses functions you +could use. If so, you have to rip the function out of the applet and make +a libbb function out of it. + +Adding a libbb function: +------------------------ + +Make a new file named .c + +----start example code------ + +#include "libbb.h" +#include "other.h" + +//kbuild:lib-y += function.o + +int function(char *a) +{ + return *a; +} + +----end example code------ + +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 +ifdefs to include or not. + +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. + +Add the config snippet to the .c file: + +//config:config MU +//config: bool "MU" +//config: default y +//config: help +//config: Returns an indeterminate value. + +Add the kbuild snippet to the .c file: + +//kbuild:lib-$(CONFIG_MU) += mu.o + + +Usage String(s) +--------------- + +Next, add usage information for your applet to the .c file. +This should look like the following: + +//usage:#define mu_trivial_usage +//usage: "[-abcde] FILE..." +//usage:#define mu_full_usage "\n\n" +//usage: "Returns an indeterminate value" +//usage: "\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. + + +Header Files +------------ + +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. + +//applet:IF_MU(APPLET(mu, BB_DIR_USR_BIN, BB_SUID_DROP)) + + +The Grand Announcement +---------------------- + +Then create a diff by adding the new files to git (remember your libbb files) + git add /mu.c +eventually also: + git add libbb/function.c +then + git commit + git format-patch HEAD^ +and send it to the mailing list: + busybox@busybox.net + http://busybox.net/mailman/listinfo/busybox + +Sending patches as attachments is preferred, but not required. diff --git a/busybox/docs/nofork_noexec.txt b/busybox/docs/nofork_noexec.txt new file mode 100644 index 00000000000000..9d210a1c9c6bac --- /dev/null +++ b/busybox/docs/nofork_noexec.txt @@ -0,0 +1,143 @@ + NOEXEC and NOFORK applets. + +Unix shells traditionally execute some commands internally in the attempt +to dramatically speed up execution. It will be slow as hell if for every +"echo blah" shell will fork and exec /bin/echo. To this end, shells +have to _reimplement_ these commands internally. + +Busybox is unique in this regard because it already is a collection +of reimplemented Unix commands, and we can do the same trick +for speeding up busybox shells, and more. NOEXEC and NOFORK applets +are exactly those applets which are eligible for these tricks. + +Applet will be subject to NOFORK/NOEXEC tricks only if it is marked +as such in applets.src.h or in their inline "//applet:" directives. + +In C, if you want to call a program and wait for it, use +spawn_and_wait(argv), BB_EXECVP(prog,argv) or BB_EXECLP(prog,argv0,...). +They check whether program name is an applet name and optionally +do NOFORK/NOEXEC thing depending on configuration. + + + Relevant CONFIG options + +FEATURE_PREFER_APPLETS + Globally enables NOFORK/NOEXEC tricks for such programs as xargs + and find: + BB_EXECVP(cmd, argv) will try to exec /proc/self/exe + if command's name matches some applet name; + spawn_and_wait(argv) will do NOFORK/NOEXEC tricks + +//TODO: the above two things probably should have separate options? + +FEATURE_SH_STANDALONE + shells will try to exec /proc/self/exe if command's name matches + some applet name; shells will do NOEXEC trick on NOEXEC applets + +//TODO: split (same as for PREFER_APPLETS) + +FEATURE_SH_NOFORK + shells will do NOFORK trick on NOFORK applets + +NB: shell builtins use these tricks regardless of FEATURE_SH_STANDALONE, +FEATURE_PREFER_APPLETS or FEATURE_SH_NOFORK. In effect, builtins +are "always NOFORK". + + + NOEXEC + +NOEXEC applet should work correctly if another applet forks and then +executes exit(_main(argc,argv)) in the child. The rules +roughly are: + +* do not expect shared global variables/buffers to be in their + "initialized" state. Examples: xfunc_error_retval can be != 1, + bb_common_bufsiz1 can be scribbled over, ... + (although usually xfunc_error_retval's state is not a problem). +* do not expect that stdio wasn't used before. Calling set[v]buf() + can be disastrous. +* ... + +NOEXEC applets save only one half of fork+exec overhead. +NOEXEC trick is disabled for NOMMU build. + + + NOFORK + +NOFORK applet should work correctly if another applet simply runs +_main(argc,argv) and then continues with its business. +xargs, find, shells do it (grep for "spawn_and_wait" and +"run_nofork_applet" to find more users). + +This poses much more serious limitations on what applet can do: + +* all NOEXEC limitations apply. +* do not run for a long time or wait for user input: + hush shell only handles signals (like ^C) after you return + from APPLET_main(). +* do not ever exit() or exec(). + - xfuncs are okay. They are using special trick to return + to the caller applet instead of dying when they detect "x" condition. + - you may "exit" to caller applet by calling xfunc_die(). Return value + is taken from xfunc_error_retval. + - fflush_stdout_and_exit(n) is ok to use. +* do not use shared global data, or save/restore shared global data + (e.g. bb_common_bufsiz1) prior to returning. + - getopt32() is ok to use. You do not need to save/restore option_mask32, + xfunc_error_retval, and logmode - it is already done by core code. +* if you allocate memory, you can use xmalloc() only on the very first + allocation. All other allocations should use malloc[_or_warn](). + After first allocation, you cannot use any xfuncs. + Otherwise, failing xfunc will return to caller applet + without freeing malloced data! +* the same applies to other resources, such as open fds: no xfuncs after + acquiring them! +* All allocated data, opened files, signal handlers, termios settings + etc should be freed/closed/restored prior to return. + +Currently, ash shell signal handling is implemented in a way that signals +have non-SA_RESTARTed handlers. This means that system calls can +return EINTR. An example of such problem is "yes" applet: +it is implemented so that it has a writing loop, this loop is exited on +any write error, and in the case of user pressing ^C the error was EINTR. +The problem is, the error causes stdout FILE* object to get into error +state, needing clearerr() - or else subsequent shell output will also +not work. ("yes" has been downgraded to NOEXEC, since hush signal handling +does not have this problem - which makes "yes" to not exit on ^C (bug). +But stray EINTRs can be seen in any NOFORK under ash, until ash is fixed). + +NOFORK applets give the most of speed advantage, but are trickiest +to implement. In order to minimize amount of bugs and maintenance, +prime candidates for NOFORK-ification are those applets which +are small and easy to audit, and those which are more likely to be +frequently executed from shell/find/xargs, particularly in shell +script loops. Applets which mess with signal handlers, termios etc +are probably not worth the effort. + +Applets which must be interruptible by ^C in shells can not be NOFORKs. + +Any NOFORK applet is also a NOEXEC applet. + + + Calling NOFORK applets + +API to call NOFORK applets is two functions: + + run_nofork_applet(appno, argv) + spawn_and_wait(argv) // only if FEATURE_PREFER_APPLETS=y + +First one is directly used by shells if FEATURE_SH_NOFORK=y. +Second one is used by many applets, but main users are xargs and find. +It itself calls run_nofork_applet(), if argv[0] is a name +of a NOFORK applet. + +run_nofork_applet() saves/inits/restores option parsing, xfunc_error_retval, +logmode, applet_name. Thus, for example, caller does not need to worry about +option_mask32 getting trashed. + + + Calling NOEXEC applets + +It's the same trusty spawn_and_wait(argv). If FEATURE_PREFER_APPLETS=y, +it does NOEXEC trick. It resets xfunc_error_retval = 1 and +logmode = LOGMODE_STDIO in the child. diff --git a/busybox/docs/posix_conformance.txt b/busybox/docs/posix_conformance.txt new file mode 100644 index 00000000000000..cdf89b744d931b --- /dev/null +++ b/busybox/docs/posix_conformance.txt @@ -0,0 +1,749 @@ + +Busybox POSIX conformance table + +See POSIX documentation (1003.1-2008) here: +http://www.opengroup.org/onlinepubs/9699919799/ +And the complete list of all utilities that POSIX covers: +http://www.opengroup.org/onlinepubs/9699919799/idx/utilities.html + +This listing is a work in progress, and currently only covers +tool options (not operands, environment variables, return codes, etc..). +For each option it is set if it (a) exists and (b) compliant to POSIX 2008. +Some options exist but there is no value in the 'compliant' column: that +means no one has yet bothered to make sure that the option does what it is +required to do. + +----------------------------------------------- + +POSIX Tools supported only as shell built-ins (ash shell): + alias, bg, cd, fg, getopts, hash, jobs, read, type, umask, ulimit, + unalias, wait, write + +POSIX Tools not supported: + asa, at, batch, bc, c99, command, compress, csplit, ex, fc, file, + gencat, getconf, iconv, join, link, locale, localedef, lp, m4, + mailx, newgrp, nl, pathchk, pax, pr, qalter, qdel, qhold, qmove, + qmsg, qrerun, qrls, qselect, qsig, qstat, qsub, tabs, talk, tput, + tsort, unlink, uucp, uustat, uux + +POSIX Tools not supported (DEVELOPMENT): + admin, cflow, ctags, cxref, delta, fort77, get, lex, make, nm, prs, rmdel, + sact, sccs, strip, unget, val, what, yacc + + +POSIX Tools supported: + +Note: echo, printf, kill, pwd documented here as stand-alone applets, + not as ash built-ins. + + +ar POSIX options ********************* Failed to recognize zip & tar (did not compare to regular ar) + option | exists | compliant | remarks + -C | no | no | + -T | no | no | + -a | no | no | + -b | no | no | + -c | no | no | + -d | no | no | + -i | no | no | + -m | no | no | + -p | yes | | + -q | no | no | + -r | no | no | + -s | no | no | + -t | yes | | + -u | no | no | + -v | yes | | + -x | yes | | +ar Busybox specific options: + -o + +awk POSIX options + option | exists | compliant | remarks + -F ERE | yes | | + -f progfile | yes | | + -v assignment | yes | | +awk Busybox specific options: None + +basename POSIX options: None +basename Busybox specific options: None + +cal POSIX options: None +cal Busybox specific options: + -y, -j + +cat POSIX options + option | exists | compliant | remarks + -u | yes | no | option is ignored +cat Busybox specific options: None + +chgrp POSIX options + option | exists | compliant | remarks + -H | yes | | + -L | yes | | + -P | yes | | + -R | yes | | + -h | yes | | +chgrp Busybox specific options: + -f, -c, -v + +chmod POSIX options + option | exists | compliant | remarks + -R | yes | yes | +chmod Busybox specific options: + -f, -v, -c + +chown POSIX options ********************************************* + option | exists | compliant | remarks + -H | yes | | It seems like all flags are supported (according to printout), but + -L | yes | | it fails to work on my machine + -P | yes | | + -R | yes | | + -h | yes | | +chown Busybox specific options: + -f, -c, -v + +cksum POSIX options: None +cksum Busybox specific options: None + +cmp POSIX options + option | exists | compliant | remarks + -l | yes | yes | + -s | yes | yes | +cmp Busybox specific options: + + +comm POSIX options + option | exists | compliant | remarks + -1 | yes | yes | + -2 | yes | yes | + -3 | yes | yes | +comm Busybox specific options: None + +cp POSIX options + option | exists | compliant | remarks + -H | yes | yes | + -L | yes | yes | + -P | yes | yes | + -R | yes | yes | + -f | yes | yes | + -i | yes | yes | + -p | yes | yes | +cp Busybox specific options: + -d, -a, -s, -c, -r, -l + +crontab POSIX options + option | exists | compliant | remarks + -e | yes | | + -l | yes | | + -r | yes | | +crontab Busybox specific options: + -u, -c + +cut POSIX options + option | exists | compliant | remarks + -b list | yes | yes | + -c list | yes | yes | + -d delim | yes | yes | + -f list | yes | yes | + -n | yes | yes | + -s | yes | yes | +cut Busybox specific options: None + +date POSIX options + option | exists | compliant | remarks + -u | yes | yes | +date Busybox specific options: + -I[SPEC], -d TIME, -r FILE, -R, -D FMT + +dd POSIX options: + option | exists | compliant | remarks + if | yes | | + of | yes | | + ibs | yes | | + obs | yes | | + bs | yes | | + cbs | no | no | + skip | yes | | + seek | yes | | + count | yes | | + conv=ascii | no | no | + conv=ebcdic | no | no | + conv=ibm | no | no | + conv=block | no | no | + conv=unblock | no | no | + conv=lcase | no | no | + conv=ucase | no | no | + conv=swap | no | no | + conv=noerror | yes | | + conv=notrunc | yes | | + conv=sync | yes | | +dd compatibility options: + conv=fsync | yes | | + iflag=skip_bytes| yes | | + iflag=fullblock | yes | | + +df POSIX options + option | exists | compliant | remarks + -P | yes | yes | + -k | yes | yes | + -t | no | no | +df Busybox specific options: + -a, -m, -B SIZE, -i, -h +Remark: +- It seems that GNU df does not round percents up in its output (thus its results are a bit different) + +diff POSIX options + option | exists | compliant | remarks + -C n | no | no | + -U n | yes | | + -b | yes | | + -c | no | no | + -e | no | no | + -f | no | no | + -r | yes | | + -u | no | no | +diff Busybox specific options: + -d, -a, -s, -t, -L, -N, -i, -T, -w, -q, -S + +dirname POSIX options: None +dirname Busybox specific options: None + +du POSIX options + option | exists | compliant | remarks + -H | yes | | + -L | yes | | + -a | yes | | + -k | yes | | + -s | yes | | + -x | yes | | +du Busybox specific options: + -c, -m, -h, -d N, -l + + +echo POSIX options: None + option | exists | compliant | remarks + -n | yes | yes | The result of -n is "implementation-defined" +echo Busybox specific options: + -e, -E + +ed POSIX options + option | exists | compliant | remarks + -p string | no | no | + -s | no | no | +ed Busybox specific options: None + +env POSIX options + option | exists | compliant | remarks + -i | no | no | +env Busybox specific options: + -u, -, -i + +expand POSIX options + option | exists | compliant | remarks + -t tablist | yes | yes | +expand Busybox specific options: + --tabs=N, -i, --initial + +expr POSIX operations: + option | exists | compliant | remarks + | | yes | yes | + & | yes | yes | + = | yes | yes | + > | yes | yes | + >= | yes | yes | + <= | yes | yes | + < | yes | yes | + != | yes | yes | + + | yes | yes | + - | yes | yes | + * | yes | yes | + / | yes | yes | + % | yes | yes | + : | yes | yes | + (expr) | yes | yes | + integer | yes | yes | + string | yes | yes | +expr Busybox specific operations: + match, substr, index, length, quote + +false POSIX options: None +false Busybox specific options: None + +find POSIX options + option | exists | compliant | remarks + -H | no | no | + -L | no | no | +find Busybox specific options: + -group NAME, -mtime DAYS, -print, -maxdepth N, -exec CMD ARG ;, -newer FILE, -context, -iname PATTERN, -follow, -depth, -xdev, -inum N, -type X, -print0, -mindepth N, -mmin MINS, -regex PATTERN, -prune, -path PATTERN, -user NAME, -delete, -perm NNN, -name PATTERN, -size N[bck] + +fold POSIX options + option | exists | compliant | remarks + -b | yes | yes | + -s | yes | yes | + -w width | yes | yes | +fold Busybox specific options: None + +fuser POSIX options + option | exists | compliant | remarks + -c | no | no | + -f | no | no | + -u | no | no | +fuser Busybox specific options: + -m, -k, -4, -SIGNAL, -6, -s + +grep POSIX options + option | exists | compliant | remarks + -E | yes | | + -F | yes | | + -c | yes | | + -e pattern_list | yes | | + -f pattern_file | yes | | + -i | yes | | + -l | yes | | + -n | yes | | + -q | yes | | + -s | yes | | + -v | yes | | + -x | no | no | +grep Busybox specific options: + -A, -C, -B, -L, -H, -o, -h, -w, -r, -z, -m MAX + +head POSIX options + option | exists | compliant | remarks + -n number | yes | yes | +head Busybox specific options: + -v, -c NUM, -q + +id POSIX options + option | exists | compliant | remarks + -G | yes | yes | + -g | yes | yes | + -n | yes | yes | + -r | yes | yes | + -u | yes | yes | +id Busybox specific options: + -Z + +ipcrm POSIX options + option | exists | compliant | remarks + -M shmkey | no | no | + -Q msgkey | no | no | + -S semkey | no | no | + -m shmid | no | no | + -q msgid | no | no | + -s semid | no | no | +ipcrm Busybox specific options: + -mM, -qQ, -sS + +ipcs POSIX options + option | exists | compliant | remarks + -a | yes | | + -b | no | no | + -c | yes | | + -m | yes | | + -o | no | no | + -p | yes | | + -q | yes | | + -s | yes | | + -t | yes | | +ipcs Busybox specific options: + -l, -i, -u + +kill POSIX options + option | exists | compliant | remarks + -l | yes | yes | + -s signal_name | yes | yes | + -signal_name | yes | yes | + -signal_number | yes | yes | +kill Busybox specific options: + -q, -o + +ln POSIX options + option | exists | compliant | remarks + -L | no | no | + -P | no | no | + -f | yes | yes | + -s | yes | yes | +ln Busybox specific options: + -S suf, -n, -b + +logger POSIX options: None +logger Busybox specific options: + -p PRIO, -t TAG, -s + +logname POSIX options: None +logname Busybox specific options: None + +ls POSIX options + option | exists | compliant | remarks + -1 | yes | yes | + -A | yes | yes | + -C | yes | yes | + -F | yes | yes | And more: '=' for sockets (not defined by POSIX) + -H | no | no | + -L | yes | yes | But coloring may be wrong (at least POSIX does not require correct colors :) ) + -R | yes | yes | + -S | yes | yes | + -a | yes | yes | + -c | yes | no | Sorts output with '-l' (should only show ctime with '-l', and sort only with '-t') + -d | yes | no | When invoked together with '-L' should read symbolic links, and doesn't + -f | no | no | + -g | no | no | + -i | yes | yes | + -k | yes | no | Does something completely unrelated! (Lists security context instead of specifying block size) + -l | yes | yes | + -m | no | no | + -n | yes | no | Works correctly only together with '-l' (but POSIX requires '-l' to be implicitly assumed) + -o | no | no | + -p | yes | yes | + -q | no | no | + -r | yes | yes | + -s | yes | yes | + -t | yes | yes | + -u | yes | yes | + -x | yes | yes | +ls Busybox specific options: + --color, -T NUM, -K, -X, -Z, -e, -h, -v, -w NUM + +man POSIX options + option | exists | compliant | remarks + -k | no | no | +man Busybox specific options: + -a Display all pages + + +mesg POSIX options: None +mesg Busybox specific options: None + +mkdir POSIX options + option | exists | compliant | remarks + -m mode | yes | yes | + -p | yes | yes | +mkdir Busybox specific options: + -Z + +mkfifo POSIX options + option | exists | compliant | remarks + -m mode | yes | yes | +mkfifo Busybox specific options: + -Z + +more POSIX options + option | exists | compliant | remarks + -c | no | no | + -e | no | no | + -i | no | no | + -n number | no | no | + -p command | no | no | + -s | no | no | + -t tagstring | no | no | + -u | no | no | +more Busybox specific options: None + +mv POSIX options + option | exists | compliant | remarks + -f | yes | yes | + -i | yes | yes | +mv Busybox specific options: None + +nice POSIX options + option | exists | compliant | remarks + -n increment | yes | yes | +nice Busybox specific options: None + +nohup POSIX options: None +nohup Busybox specific options: None + +od POSIX options + option | exists | compliant | remarks + -A address_base | no | no | + -N count | no | no | + -b | no | no | + -c | no | no | + -d | no | no | + -j skip | no | no | + -o | no | no | + -s | no | no | + -t type_string | no | no | + -v | no | no | + -x | no | no | +od Busybox specific options: None + +paste POSIX options + option | exists | compliant | remarks + -d list | yes | yes | + -s | yes | yes | +paste Busybox specific options: None + +patch POSIX options + option | exists | compliant | remarks + -D define | no | no | + -N | no | no | + -R | yes | yes | + -b | no | no | + -c | no | no | + -d dir | no | no | + -e | no | no | + -i patchfile | yes | yes | + -l | no | no | + -n | no | no | + -o outfile | no | no | + -p num | yes | yes | + -r rejectfile | no | no | + -u | no | no | +patch Busybox specific options: None + +printf POSIX options: None +printf Busybox specific options: None + +ps POSIX options + option | exists | compliant | remarks + -A | no | no | + -G grouplist | no | no | + -U userlist | no | no | + -a | no | no | + -d | no | no | + -e | no | no | + -f | no | no | + -g grouplist | no | no | + -l | no | no | + -n namelist | no | no | + -o format | yes | no | not supported: ruser, group, rgroup, pcpu + -p proclist | no | no | + -t termlist | no | no | + -u userlist | no | no | +ps Busybox specific options: None + +pwd POSIX options + option | exists | compliant | remarks + -L | no | no | + -P | no | no | +pwd Busybox specific options: None + +renice POSIX options + option | exists | compliant | remarks + -g | yes | yes | + -n increment | yes | yes | Note POSIX allows only to run with this option (busybox also allows to run without '-n' and set niceness directly) + -p | yes | yes | + -u | yes | yes | +renice Busybox specific options: None + +rm POSIX options + option | exists | compliant | remarks + -R | yes | yes | + -f | yes | yes | + -i | yes | yes | + -r | yes | yes | +rm Busybox specific options: None + +rmdir POSIX options + option | exists | compliant | remarks + -p | yes | yes | +rmdir Busybox specific options: + --parents + +sed POSIX options + option | exists | compliant | remarks + -e script | yes | | + -f script_file | yes | | + -n | yes | | +sed Busybox specific options: + -i, -r + +sh POSIX options + option | exists | compliant | remarks + -c | no | no | + -i | no | no | + -s | no | no | +sh Busybox specific options: None + +sleep POSIX options: None +sleep Busybox specific options: None + +sort POSIX options + option | exists | compliant | remarks + -C | no | no | + -b | yes | yes | + -c | yes | yes | + -d | yes | yes | + -f | yes | yes | + -i | yes | yes | But is not like GNU sort, which isn't! (try to sort 'a\nA\nB\nb' with and without -f) + -k keydef | yes | | + -m | no | no | + -n | yes | yes | + -o output | yes | yes | + -r | yes | yes | + -t char | yes | | + -u | yes | yes | +sort Busybox specific options: + -mST, -g, -M, -s, -z + +split POSIX options + option | exists | compliant | remarks + -a suffix_length | yes | yes | + -b n | yes | yes | + -b nk | yes | yes | + -b nm | yes | yes | + -l line_count | yes | yes | +split Busybox specific options: None + +strings POSIX options + option | exists | compliant | remarks + -a | yes | yes | + -n number | yes | yes | + -t format | no | no | +strings Busybox specific options: + -o, -f + +stty POSIX options + option | exists | compliant | remarks + -a | yes | yes | + -g | yes | yes | +stty Busybox specific options: + -F DEVICE + +tail POSIX options + option | exists | compliant | remarks + -c number | yes | yes | + -f | yes | yes | + -n number | yes | yes | +tail Busybox specific options: + -v, -q, -s SEC + +tee POSIX options + option | exists | compliant | remarks + -a | yes | yes | + -i | yes | yes | +tee Busybox specific options: None + +test POSIX options: None +test Busybox specific options: None + +time POSIX options + option | exists | compliant | remarks + -p | no | no | +time Busybox specific options: + -v + +touch POSIX options + option | exists | compliant | remarks + -a | no | no | + -c | yes | yes | + -d date_time | no | no | + -m | no | no | + -r ref_file | no | no | + -t time | no | no | +touch Busybox specific options: None + +tr POSIX options + option | exists | compliant | remarks + -C | no | no | + -c | yes | yes | + -d | yes | yes | + -s | yes | yes | +tr Busybox specific options: None + +true POSIX options: None +true Busybox specific options: None + +tty POSIX options: None +tty Busybox specific options: + -s + +uname POSIX options + option | exists | compliant | remarks + -a | yes | yes | + -m | yes | yes | + -n | yes | yes | + -r | yes | yes | + -s | yes | yes | + -v | yes | yes | +uname Busybox specific options: + -p + +uncompress POSIX options + option | exists | compliant | remarks + -c | yes | yes | + -f | yes | yes | + -v | no | no | +uncompress Busybox specific options: None + +unexpand POSIX options + option | exists | compliant | remarks + -a | yes | no | POSIX requires converting two or more spaces to tabs, busybox converts one or more spaces + -t tablist | yes | yes | +unexpand Busybox specific options: + --tabs=N, -f, --first-only, --all + +uniq POSIX options + option | exists | compliant | remarks + -c | yes | yes | + -d | yes | yes | + -f fields | yes | yes | + -s chars | yes | yes | + -u | yes | yes | +uniq Busybox specific options: + -w N + +uudecode POSIX options + option | exists | compliant | remarks + -o outfile | no | no | +uudecode Busybox specific options: None + +uuencode POSIX options + option | exists | compliant | remarks + -m | yes | yes | +uuencode Busybox specific options: None + +vi POSIX options + option | exists | compliant | remarks + -R | yes | | + -c command | yes | | + -r | no | no | + -t tagstring | no | no | + -w size | no | no | +vi Busybox specific options: + -H + +wc POSIX options + option | exists | compliant | remarks + -c | yes | yes | + -l | yes | yes | + -m | no | no | + -w | yes | yes | +wc Busybox specific options: + -L + +who POSIX options + option | exists | compliant | remarks + -H | no | no | + -T | no | no | + -a | yes | no | just shows all + -b | no | no | + -d | no | no | + -l | no | no | + -m | no | no | + -p | no | no | + -q | no | no | + -r | no | no | + -s | no | no | + -t | no | no | + -u | no | no | +who Busybox specific options: None + +xargs POSIX options + option | exists | compliant | remarks + -E eofstr | no | no | + -I replstr | no | no | + -L number | no | no | + -n number | yes | yes | + -p | yes | yes | + -s size | yes | yes | + -t | yes | yes | + -x | yes | yes | +xargs Busybox specific options: + -e[STR], -0, -r + +zcat POSIX options: None +zcat Busybox specific options: None diff --git a/busybox/docs/sigint.htm b/busybox/docs/sigint.htm new file mode 100644 index 00000000000000..d656aeb8ce3611 --- /dev/null +++ b/busybox/docs/sigint.htm @@ -0,0 +1,627 @@ + + + +Proper handling of SIGINT/SIGQUIT [http://www.cons.org/cracauer/sigint.html] + + +

Proper handling of SIGINT/SIGQUIT

+ +

+ + + + + +
Abstract: +In UNIX terminal sessions, you usually have a key like +C-c (Control-C) to immediately end whatever program you +have running in the foreground. This should work even when the program +you called has called other programs in turn. Everything should be +aborted, giving you your command prompt back, no matter how deep the +call stack is. + +

Basically, it's trivial. But the existence of interactive +applications that use SIGINT and/or SIGQUIT for other purposes than a +complete immediate abort make matters complicated, and - as was to +expect - left us with several ways to solve the problems. Of course, +existing shells and applications follow different ways. + +

This Web pages outlines different ways to solve the problem and +argues that only one of them can do everything right, although it +means that we have to fix some existing software. + + + +

Intended audience: Programmers who implement programs that catch SIGINT/SIGQUIT. +
Programmers who implements shells or shell-like programs that +execute batches of programs. + +

Users who have problems problems getting rid of runaway shell +scripts using Control-C. Or have interactive applications +that don't behave right when sending SIGINT. Examples are emacs'es +that die on Control-g or shellscript statements that sometimes are +executed and sometimes not, apparently not determined by the user's +intention. + + +

Required knowledge: You have to know what it means to catch SIGINT or SIGQUIT and how +processes are waiting for other processes (children) they spawned. + + +
+ + + +

Basic concepts

+ +What technically happens when you press Control-C is that all programs +running in the foreground in your current terminal (or virtual +terminal) get the signal SIGINT sent. + +

You may change the key that triggers the signal using +stty and running programs may remap the SIGINT-sending +key at any time they like, without your intervention and without +asking you first. + +

The usual reaction of a running program to SIGINT is to exit. +However, not all program do an exit on SIGINT, programs are free to +use the signal for other actions or to ignore it at all. + +

All programs running in the foreground receive the signal. This may +be a nested "stack" of programs: You started a program that started +another and the outer is waiting for the inner to exit. This nesting +may be arbitrarily deep. + +

The innermost program is the one that decides what to do on SIGINT. +It may exit, do something else or do nothing. Still, when the user hit +SIGINT, all the outer programs are awaken, get the signal and may +react on it. + +

What we try to achieve

+ +The problem is with shell scripts (or similar programs that call +several subprograms one after another). + +

Let us consider the most basic script: +

+#! /bin/sh
+program1
+program2
+
+and the usual run looks like this: +
+$ sh myscript
+[output of program1]
+[output of program2]
+$
+
+ +

Let us assume that both programs do nothing special on SIGINT, they +just exit. + +

Now imagine the user hits C-c while a shellscript is executing its +first program. The following programs receive SIGINT: program1 and +also the shell executing the script. program1 exits. + +

But what should the shell do? If we say that it is only the +innermost's programs business to react on SIGINT, the shell will do +nothing special (not exit) and it will continue the execution of the +script and run program2. But this is wrong: The user's intention in +hitting C-c is to abort the whole script, to get his prompt back. If +he hits C-c while the first program is running, he does not want +program2 to be even started. + +

here is what would happen if the shell doesn't do anything: +

+$ sh myscript
+[first half of program1's output]
+C-c   [users presses C-c]
+[second half of program1's output will not be displayed]
+[output of program2 will appear]
+
+ + +

Consider a more annoying example: +

+#! /bin/sh
+# let's assume there are 300 *.dat files
+for file in *.dat ; do
+	dat2ascii $dat
+done
+
+ +If your shell wouldn't end if the user hits C-c, +C-c would just end one dat2ascii run and +the script would continue. Thus, you had to hit C-c up to +300 times to end this script. + +

Alternatives to do so

+ +

There are several ways to handle abortion of shell scripts when +SIGINT is received while a foreground child runs: + +

+ +
  • As just outlined, the shellscript may just continue, ignoring the +fact that the user hit C-c. That way, your shellscript - +including any loops - would continue and you had no chance of aborting +it except using the kill command after finding out the outermost +shell's PID. This "solution" will not be discussed further, as it is +obviously not desirable. + +

  • The shell itself exits immediately when it receives SIGINT. Not +only the program called will exit, but the calling (the +script-executing) shell. The first variant is to exit the shell (and +therefore discontinuing execution of the script) immediately, while +the background program may still be executing (remember that although +the shell is just waiting for the called program to exit, it is woken +up and may act). I will call the way of doing things the "IUE" (for +"immediate unconditional exit") for the rest of this document. + +

  • As a variant of the former, when the shell receives SIGINT +while it is waiting for a child to exit, the shell does not exit +immediately. but it remembers the fact that a SIGINT happened. After +the called program exits and the shell's wait ends, the shell will +exit itself and hence discontinue the script. I will call the way of +doing things the "WUE" (for "wait and unconditional exit") for the +rest of this document. + +

  • There is also a way that the calling shell can tell whether the +called program exited on SIGINT and if it ignored SIGINT (or used it +for other purposes). As in the WUE way, the shell waits for +the child to complete. It figures whether the program was ended on +SIGINT and if so, it discontinue the script. If the program did any +other exit, the script will be continued. I will call the way of doing +things the "WCE" (for "wait and cooperative exit") for the rest of +this document. + +
  • + +

    The problem

    + +On first sight, all three solutions (IUE, WUE and WCE) all seem to do +what we want: If C-c is hit while the first program of the shell +script runs, the script is discontinued. The user gets his prompt back +immediately. So what are the difference between these way of handling +SIGINT? + +

    There are programs that use the signal SIGINT for other purposes +than exiting. They use it as a normal keystroke. The user is expected +to use the key that sends SIGINT during a perfectly normal program +run. As a result, the user sends SIGINT in situations where he/she +does not want the program or the script to end. + +

    The primary example is the emacs editor: C-g does what ESC does in +other applications: It cancels a partially executed or prepared +operation. Technically, emacs remaps the key that sends SIGINT from +C-c to C-g and catches SIGINT. + +

    Remember that the SIGINT is sent to all programs running in the +foreground. If emacs is executing from a shell script, both emacs and +the shell get SIGINT. emacs is the program that decides what to do: +Exit on SIGINT or not. emacs decides not to exit. The problem arises +when the shell draws its own conclusions from receiving SIGINT without +consulting emacs for its opinion. + +

    Consider this script: +

    +#! /bin/sh
    +emacs /tmp/foo
    +cp /tmp/foo /home/user/mail/sent
    +
    + +

    If C-g is used in emacs, both the shell and emacs will received +SIGINT. Emacs will not exit, the user used C-g as a normal editing +keystroke, he/she does not want the script to be aborted on C-g. + +

    The central problem is that the second command (cp) may +unintentionally be killed when the shell draws its own conclusion +about the user's intention. The innermost program is the only one to +judge. + +

    One more example

    + +

    Imagine a mail session using a curses mailer in a tty. You called +your mailer and started to compose a message. Your mailer calls emacs. +C-g is a normal editing key in emacs. Technically it +sends SIGINT (it was C-c, but emacs remapped the key) to +

    +
  • emacs +
  • the shell between your mailer and emacs, the one from your mailers + system("emacs /tmp/bla.44") command +
  • the mailer itself +
  • possibly another shell if your mailer was called by a shell script +or from another application using system(3) +
  • your interactive shell (which ignores it since it is interactive +and hence is not relevant to this discussion) +
  • + +

    If everyone just exits on SIGINT, you will be left with nothing but +your login shell, without asking. + +

    But for sure you don't want to be dropped out of your editor and +out of your mailer back to the commandline, having your edited data +and mailer status deleted. + +

    Understand the difference: While C-g is used an a kind +of abort key in emacs, it isn't the major "abort everything" key. When +you use C-g in emacs, you want to end some internal emacs +command. You don't want your whole emacs and mailer session to end. + +

    So, if the shell exits immediately if the user sends SIGINT (the +second of the four ways shown above), the parent of emacs would die, +leaving emacs without the controlling tty. The user will lose it's +editing session immediately and unrecoverable. If the "main" shell of +the operating system defaults to this behavior, every editor session +that is spawned from a mailer or such will break (because it is +usually executed by system(3), which calls /bin/sh). This was the case +in FreeBSD before I and Bruce Evans changed it in 1998. + +

    If the shell recognized that SIGINT was sent and exits after the +current foreground process exited (the third way of the four), the +editor session will not be disturbed, but things will still not work +right. + +

    A further look at the alternatives

    + +

    Still considering this script to examine the shell's actions in the +IUE, WUE and ICE way of handling SIGINT: +

    +#! /bin/sh
    +emacs /tmp/foo
    +cp /tmp/foo /home/user/mail/sent
    +
    + +

    The IUE ("immediate unconditional exit") way does not work at all: +emacs wants to survive the SIGINT (it's a normal editing key for +emacs), but its parent shell unconditionally thinks "We received +SIGINT. Abort everything. Now.". The shell will exit even before emacs +exits. But this will leave emacs in an unusable state, since the death +of its calling shell will leave it without required resources (file +descriptors). This way does not work at all for shellscripts that call +programs that use SIGINT for other purposes than immediate exit. Even +for programs that exit on SIGINT, but want to do some cleanup between +the signal and the exit, may fail before they complete their cleanup. + +

    It should be noted that this way has one advantage: If a child +blocks SIGINT and does not exit at all, this way will get control back +to the user's terminal. Since such programs should be banned from your +system anyway, I don't think that weighs against the disadvantages. + +

    WUE ("wait and unconditional exit") is a little more clever: If C-g +was used in emacs, the shell will get SIGINT. It will not immediately +exit, but remember the fact that a SIGINT happened. When emacs ends +(maybe a long time after the SIGINT), it will say "Ok, a SIGINT +happened sometime while the child was executing, the user wants the +script to be discontinued". It will then exit. The cp will not be +executed. But that's bad. The "cp" will be executed when the emacs +session ended without the C-g key ever used, but it will not be +executed when the user used C-g at least one time. That is clearly not +desired. Since C-g is a normal editing key in emacs, the user expects +the rest of the script to behave identically no matter what keys he +used. + +

    As a result, the "WUE" way is better than the "IUE" way in that it +does not break SIGINT-using programs completely. The emacs session +will end undisturbed. But it still does not support scripts where +other actions should be performed after a program that use SIGINT for +non-exit purposes. Since the behavior is basically undeterminable for +the user, this can lead to nasty surprises. + +

    The "WCE" way fixes this by "asking" the called program whether it +exited on SIGINT or not. While emacs receives SIGINT, it does not exit +on it and a calling shell waiting for its exit will not be told that +it exited on SIGINT. (Although it receives SIGINT at some point in +time, the system does not enforce that emacs will exit with +"I-exited-on-SIGINT" status. This is under emacs' control, see below). + +

    this still work for the normal script without SIGINT-using +programs:

    +
    +#! /bin/sh
    +program1
    +program2
    +
    + +Unless program1 and program2 mess around with signal handling, the +system will tell the calling shell whether the programs exited +normally or as a result of SIGINT. + +

    The "WCE" way then has an easy way to things right: When one called +program exited with "I-exited-on-SIGINT" status, it will discontinue +the script after this program. If the program ends without this +status, the next command in the script is started. + +

    It is important to understand that a shell in "WCE" modus does not +need to listen to the SIGINT signal at all. Both in the +"emacs-then-cp" script and in the "several-normal-programs" script, it +will be woken up and receive SIGINT when the user hits the +corresponding key. But the shell does not need to react on this event +and it doesn't need to remember the event of any SIGINT, either. +Telling whether the user wants to end a script is done by asking that +program that has to decide, that program that interprets keystrokes +from the user, the innermost program. + +

    So everything is well with WCE?

    + +Well, almost. + +

    The problem with the "WCE" modus is that there are broken programs +that do not properly communicate the required information up to the +calling program. + +

    Unless a program messes with signal handling, the system does this +automatically. + +

    There are programs that want to exit on SIGINT, but they don't let +the system do the automatic exit, because they want to do some +cleanup. To do so, they catch SIGINT, do the cleanup and then exit by +themselves. + +

    And here is where the problem arises: Once they catch the signal, +the system will no longer communicate the "I-exited-on-SIGINT" status +to the calling program automatically. Even if the program exit +immediately in the signal handler of SIGINT. Once it catches the +signal, it has to take care of communicating the signal status +itself. + +

    Some programs don't do this. On SIGINT, they do cleanup and exit +immediately, but the calling shell isn't told about the non-normal exit +and it will call the next program in the script. + +

    As a result, the user hits SIGINT and while one program exits, the +shellscript continues. To him/her it looks like the shell fails to +obey to his abortion command. + +

    Both IUE or WUE shell would not have this problem, since they +discontinue the script on their own. But as I said, they don't support +programs using SIGINT for non-exiting purposes, no matter whether +these programs properly communicate their signal status to the calling +shell or not. + +

    Since some shell in wide use implement the WUE way (and some even +IUE), there is a considerable number of broken programs out there that +break WCE shells. The programmers just don't recognize it if their +shell isn't WCE. + +

    How to be a proper program

    + +

    (Short note in advance: What you need to achieve is that +WIFSIGNALED(status) is true in the calling program and that +WTERMSIG(status) returns SIGINT.) + +

    If you don't catch SIGINT, the system automatically does the right +thing for you: Your program exits and the calling program gets the +right "I-exited-on-SIGINT" status after waiting for your exit. + +

    But once you catch SIGINT, you have to act. + +

    Decide whether the SIGINT is used for exit/abort purposes and hence +a shellscript calling this program should discontinue. This is +hopefully obvious. If you just need to do some cleanup on SIGINT, but +then exit immediately, the answer is "yes". + +

    If so, you have to tell the calling program about it by exiting +with the "I-exited-on-SIGINT" status. + +

    There is no other way of doing this than to kill yourself with a +SIGINT signal. Do it by resetting the SIGINT handler to SIG_DFL, then +send yourself the signal. + +

    +void sigint_handler(int sig)
    +{
    +	
    +	signal(SIGINT, SIG_DFL);
    +	kill(getpid(), SIGINT);
    +}
    +
    + +Notes: + + + +
  • You cannot "fake" the proper exit status by an exit(3) with a +special numeric value. People often assume this since the manuals for +shells often list some return value for exactly this. But this is just +a convention for your shell script. It does not work from one UNIX API +program to another. + +

    All that happens is that the shell sets the "$?" variable to a +special numeric value for the convenience of your script, because your +script does not have access to the lower-lever UNIX status evaluation +functions. This is just an agreement between your script and the +executing shell, it does not have any meaning in other contexts. + +

  • Do not use kill(0, SIGINT) without consulting the manul for +your OS implementation. I.e. on BSD, this would not send the signal to +the current process, but to all processes in the group. + +

  • POSIX 1003.1 allows all these calls to appear in signal +handlers, so it is portable. + +
  • + +

    In a bourne shell script, you can catch signals using the +trap command. Here, the same as for C programs apply. If +the intention of SIGINT is to end your program, you have to exit in a +way that the calling programs "sees" that you have been killed. If +you don't catch SIGINT, this happened automatically, but of you catch +SIGINT, i.e. to do cleanup work, you have to end the program by +killing yourself, not by calling exit. + +

    Consider this example from FreeBSD's mkdep, which is a +bourne shell script. + +

    +TMP=_mkdep$$
    +trap 'rm -f $TMP ; trap 2 ; kill -2 $$' 1 2 3 13 15
    +
    + +Yes, you have to do it the hard way. It's even more annoying in shell +scripts than in C programs since you can't "pre-delete" temporary +files (which isn't really portable in C, though). + +

    All this applies to programs in all languages, not only C and +bourne shell. Every language implementation that lets you catch SIGINT +should also give you the option to reset the signal and kill yourself. + +

    It is always desirable to exit the right way, even if you don't +expect your usual callers to depend on it, some unusual one will come +along. This proper exit status will be needed for WCE and will not +hurt when the calling shell uses IUE or WUE. + +

    How to be a proper shell

    + +All this applies only for the script-executing case. Most shells will +also have interactive modes where things are different. + + + +
  • Do nothing special when SIGINT appears while you wait for a child. +You don't even have to remember that one happened. + +

  • Wait for child to exit, get the exit status. Do not truncate it +to type char. + +

  • Look at WIFSIGNALED(status) and WTERMSIG(status) to tell +whether the child says "I exited on SIGINT: in my opinion the user +wants the shellscript to be discontinued". + +

  • If the latter applies, discontinue the script. + +

  • Exit. But since a shellscript may in turn be called by a +shellscript, you need to make sure that you properly communicate the +discontinue intention to the calling program. As in any other program +(see above), do + +
    +	signal(SIGINT, SIG_DFL);
    +	kill(getpid(), SIGINT);
    +
    + +
  • + +

    Other remarks

    + +Although this web page talks about SIGINT only, almost the same issues +apply to SIGQUIT, including proper exiting by killing yourself after +catching the signal and proper reaction on the WIFSIGNALED(status) +value. One notable difference for SIGQUIT is that you have to make +sure that not the whole call tree dumps core. + +

    What to fight

    + +Make sure all programs really kill themselves if they react +to SIGINT or SIGQUIT and intend to abort their operation as a result +of this signal. Programs that don't use SIGINT/SIGQUIT as a +termination trigger - but as part of normal operation - don't kill +themselves, but do a normal exit instead. + +

    Make sure people understand why you can't fake an exit-on-signal by +doing exit(...) using any numerical status. + +

    Make sure you use a shell that behaves right. Especially if you +develop programs, since it will help seeing problems. + +

    Concrete examples how to fix programs:

    +
      + +
    • The fix for FreeBSD's +time(1). This fix is the best example, it's quite short and clear and +it fixes a case where someone tried to fake signal exit status by a +numerical value. And the complete program is small. + +

    • Fix for FreeBSD's +truss(1). + +

    • The fix for FreeBSD's +mkdep(1), a shell script. + + +

    • Fix for FreeBSD's make(1), part 1, +part 2. + +
    + +

    Testsuite for shells

    + +I have a collection of shellscripts that test shells for the +behavior. See my download dir to get the newest +"sh-interrupt" files, either as a tarfile or as individual file for +online browsing. This isn't really documented, besides from the +comments the scripts echo. + +

    Appendix 1 - table of implementation choices

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Method signDoes what?Example shells that implement it:What happens when a shellscript called emacs, the user used +C-g and the script has additional commands in it?What happens when a shellscript called emacs, the user did not use +C-c and the script has additional commands in it?What happens if a non-interactive child catches SIGINT?To behave properly, children must do what?
    IUEThe shell executing a script exits immediately if it receives +SIGINT.4.4BSD ash (ash), NetBSD, FreeBSD prior to 3.0/22.8The editor session is lost and subsequent commands are not +executed.The editor continues as normal and the subsequent commands are +executed. The scripts ends immediately, returning to the caller even before +the current foreground child of the shell exits. It doesn't matter what the child does or how it exits, even if the +child continues to operate, the shell returns.
    WUEIf the shell executing a script received SIGINT while a foreground +process was running, it will exit after that child's exit.pdksh (OpenBSD /bin/sh)The editor continues as normal, but subsequent commands from the +script are not executed.The editor continues as normal and subsequent commands are +executed. The scripts returns to its caller after the current foreground +child exits, no matter how the child exited. It doesn't matter how the child exits (signal status or not), but +if it doesn't return at all, the shell will not return. In no case +will further commands from the script be executed.
    WCEThe shell exits if a child signaled that it was killed on a +signal (either it had the default handler for SIGINT or it killed +itself). bash (Linux /bin/sh), most commercial /bin/sh, FreeBSD /bin/sh +from 3.0/2.2.8.The editor continues as normal and subsequent commands are +executed. The editor continues as normal and subsequent commands are +executed. The scripts returns to its caller after the current foreground +child exits, but only if the child exited with signal status. If +the child did a normal exit (even if it received SIGINT, but catches +it), the script will continue. The child must be implemented right, or the user will not be able +to break shell scripts reliably.
    + +

     +
    ©2005 Martin Cracauer <cracauer @ cons.org> +http://www.cons.org/cracauer/ +
    Last changed: $Date: 2005/02/11 21:44:43 $ + diff --git a/busybox/docs/smallint.txt b/busybox/docs/smallint.txt new file mode 100644 index 00000000000000..b57dfd7753fed3 --- /dev/null +++ b/busybox/docs/smallint.txt @@ -0,0 +1,39 @@ + smalluint i = index_in_str_array(params, name) + 1; + if (i == 0) + return 0; + if (!(i == 4 || i == 5)) + i |= 0x80; + + return i; + +I think that this optimization is wrong. +index_in_str_array returns int. At best, compiler will use it as-is. +At worst, compiler will try to make sure that it is properly cast +into a byte, which probably results in "n = n & 0xff" on many architectures. + +You save nothing on space here because i is not stored on-stack, +gcc will keep it in register. And even if it *is* stored, +it is *stack* storage, which is cheap (unlike data/bss). + +small[u]ints are useful _mostly_ for: + +(a) flag variables + (a1) global flag variables - make data/bss smaller + (a2) local flag variables - "a = 5", "a |= 0x40" are smaller + for bytes than for full integers. + Example: + on i386, there is no widening constant store instruction + for some types of address modes, thus + movl $0x0,(%eax) is "c7 00 00 00 00 00" + movb $0x0,(%eax) is "c6 00 00" +(b) small integer structure members, when you have many such + structures allocated, + or when these are global objects of this structure type + +small[u]ints are *NOT* useful for: + +(a) function parameters and return values - + they are pushed on-stack or stored in registers, bytes here are *harder* + to deal with than ints +(b) "computational" variables - "a++", "a = b*3 + 7" may take more code to do + on bytes than on ints on some architectires. diff --git a/busybox/docs/style-guide.txt b/busybox/docs/style-guide.txt new file mode 100644 index 00000000000000..9eed7f125156d6 --- /dev/null +++ b/busybox/docs/style-guide.txt @@ -0,0 +1,713 @@ +Busybox Style Guide +=================== + +This document describes the coding style conventions used in Busybox. If you +add a new file to Busybox or are editing an existing file, please format your +code according to this style. If you are the maintainer of a file that does +not follow these guidelines, please -- at your own convenience -- modify the +file(s) you maintain to bring them into conformance with this style guide. +Please note that this is a low priority task. + +To help you format the whitespace of your programs, an ".indent.pro" file is +included in the main Busybox source directory that contains option flags to +format code as per this style guide. This way you can run GNU indent on your +files by typing 'indent myfile.c myfile.h' and it will magically apply all the +right formatting rules to your file. Please _do_not_ run this on all the files +in the directory, just your own. + + + +Declaration Order +----------------- + +Here is the preferred order in which code should be laid out in a file: + + - commented program name and one-line description + - commented author name and email address(es) + - commented GPL boilerplate + - commented longer description / notes for the program (if needed) + - #includes of .h files with angle brackets (<>) around them + - #includes of .h files with quotes ("") around them + - #defines (if any, note the section below titled "Avoid the Preprocessor") + - const and global variables + - function declarations (if necessary) + - function implementations + + + +Whitespace and Formatting +------------------------- + +This is everybody's favorite flame topic so let's get it out of the way right +up front. + + +Tabs vs. Spaces in Line Indentation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The preference in Busybox is to indent lines with tabs. Do not indent lines +with spaces and do not indents lines using a mixture of tabs and spaces. (The +indentation style in the Apache and Postfix source does this sort of thing: +\s\s\s\sif (expr) {\n\tstmt; --ick.) The only exception to this rule is +multi-line comments that use an asterisk at the beginning of each line, i.e.: + + \t/* + \t * This is a block comment. + \t * Note that it has multiple lines + \t * and that the beginning of each line has a tab plus a space + \t * except for the opening '/*' line where the slash + \t * is used instead of a space. + \t */ + +Furthermore, The preference is that tabs be set to display at four spaces +wide, but the beauty of using only tabs (and not spaces) at the beginning of +lines is that you can set your editor to display tabs at *whatever* number of +spaces is desired and the code will still look fine. + + +Operator Spacing +~~~~~~~~~~~~~~~~ + +Put spaces between terms and operators. Example: + + Don't do this: + + for(i=0;i 0) + + +Bracket Spacing +~~~~~~~~~~~~~~~ + +If an opening bracket starts a function, it should be on the +next line with no spacing before it. However, if a bracket follows an opening +control block, it should be on the same line with a single space (not a tab) +between it and the opening control block statement. Examples: + + Don't do this: + + while (!done) + { + + do + { + + Don't do this either: + + while (!done){ + + do{ + + And for heaven's sake, don't do this: + + while (!done) + { + + do + { + + Do this instead: + + while (!done) { + + do { + +If you have long logic statements that need to be wrapped, then uncuddling +the bracket to improve readability is allowed. Generally, this style makes +it easier for reader to notice that 2nd and following lines are still +inside 'if': + + if (some_really_long_checks && some_other_really_long_checks + && some_more_really_long_checks + && even_more_of_long_checks + ) { + do_foo_now; + +Spacing around Parentheses +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Put a space between C keywords and left parens, but not between function names +and the left paren that starts it's parameter list (whether it is being +declared or called). Examples: + + Don't do this: + + while(foo) { + for(i = 0; i < n; i++) { + + Do this instead: + + while (foo) { + for (i = 0; i < n; i++) { + + But do functions like this: + + static int my_func(int foo, char bar) + ... + baz = my_func(1, 2); + +Also, don't put a space between the left paren and the first term, nor between +the last arg and the right paren. + + Don't do this: + + if ( x < 1 ) + strcmp( thisstr, thatstr ) + + Do this instead: + + if (x < 1) + strcmp(thisstr, thatstr) + + +Cuddled Elses +~~~~~~~~~~~~~ + +Also, please "cuddle" your else statements by putting the else keyword on the +same line after the right bracket that closes an 'if' statement. + + Don't do this: + + if (foo) { + stmt; + } + else { + stmt; + } + + Do this instead: + + if (foo) { + stmt; + } else { + stmt; + } + +The exception to this rule is if you want to include a comment before the else +block. Example: + + if (foo) { + stmts... + } + /* otherwise, we're just kidding ourselves, so re-frob the input */ + else { + other_stmts... + } + + +Labels +~~~~~~ + +Labels should start at the beginning of the line, not indented to the block +level (because they do not "belong" to block scope, only to whole function). + + if (foo) { + stmt; + label: + stmt2; + stmt; + } + +(Putting label at position 1 prevents diff -p from confusing label for function +name, but it's not a policy of busybox project to enforce such a minor detail). + + + +Variable and Function Names +--------------------------- + +Use the K&R style with names in all lower-case and underscores occasionally +used to separate words (e.g., "variable_name" and "numchars" are both +acceptable). Using underscores makes variable and function names more readable +because it looks like whitespace; using lower-case is easy on the eyes. + + Frowned upon: + + hitList + TotalChars + szFileName + pf_Nfol_TriState + + Preferred: + + hit_list + total_chars + file_name + sensible_name + +Exceptions: + + - Enums, macros, and constant variables are occasionally written in all + upper-case with words optionally separated by underscores (i.e. FIFO_TYPE, + ISBLKDEV()). + + - Nobody is going to get mad at you for using 'pvar' as the name of a + variable that is a pointer to 'var'. + + +Converting to K&R +~~~~~~~~~~~~~~~~~ + +The Busybox codebase is very much a mixture of code gathered from a variety of +sources. This explains why the current codebase contains such a hodge-podge of +different naming styles (Java, Pascal, K&R, just-plain-weird, etc.). The K&R +guideline explained above should therefore be used on new files that are added +to the repository. Furthermore, the maintainer of an existing file that uses +alternate naming conventions should, at his own convenience, convert those +names over to K&R style. Converting variable names is a very low priority +task. + +If you want to do a search-and-replace of a single variable name in different +files, you can do the following in the busybox directory: + + $ perl -pi -e 's/\bOldVar\b/new_var/g' *.[ch] + +If you want to convert all the non-K&R vars in your file all at once, follow +these steps: + + - In the busybox directory type 'examples/mk2knr.pl files-to-convert'. This + does not do the actual conversion, rather, it generates a script called + 'convertme.pl' that shows what will be converted, giving you a chance to + review the changes beforehand. + + - Review the 'convertme.pl' script that gets generated in the busybox + directory and remove / edit any of the substitutions in there. Please + especially check for false positives (strings that should not be + converted). + + - Type './convertme.pl same-files-as-before' to perform the actual + conversion. + + - Compile and see if everything still works. + +Please be aware of changes that have cascading effects into other files. For +example, if you're changing the name of something in, say utility.c, you +should probably run 'examples/mk2knr.pl utility.c' at first, but when you run +the 'convertme.pl' script you should run it on _all_ files like so: +'./convertme.pl *.[ch]'. + + + +Avoid The Preprocessor +---------------------- + +At best, the preprocessor is a necessary evil, helping us account for platform +and architecture differences. Using the preprocessor unnecessarily is just +plain evil. + + +The Folly of #define +~~~~~~~~~~~~~~~~~~~~ + +Use 'const var' for declaring constants. + + Don't do this: + + #define CONST 80 + + Do this instead, when the variable is in a header file and will be used in + several source files: + + enum { CONST = 80 }; + +Although enum may look ugly to some people, it is better for code size. +With "const int" compiler may fail to optimize it out and will reserve +a real storage in rodata for it! (Hopefully, newer gcc will get better +at it...). With "define", you have slight risk of polluting namespace +(#define doesn't allow you to redefine the name in the inner scopes), +and complex "define" are evaluated each time they used, not once +at declarations like enums. Also, the preprocessor does _no_ type checking +whatsoever, making it much more error prone. + + +The Folly of Macros +~~~~~~~~~~~~~~~~~~~ + +Use 'static inline' instead of a macro. + + Don't do this: + + #define mini_func(param1, param2) (param1 << param2) + + Do this instead: + + static inline int mini_func(int param1, param2) + { + return (param1 << param2); + } + +Static inline functions are greatly preferred over macros. They provide type +safety, have no length limitations, no formatting limitations, have an actual +return value, and under gcc they are as cheap as macros. Besides, really long +macros with backslashes at the end of each line are ugly as sin. + + +The Folly of #ifdef +~~~~~~~~~~~~~~~~~~~ + +Code cluttered with ifdefs is difficult to read and maintain. Don't do it. +Instead, put your ifdefs at the top of your .c file (or in a header), and +conditionally define 'static inline' functions, (or *maybe* macros), which are +used in the code. + + Don't do this: + + ret = my_func(bar, baz); + if (!ret) + return -1; + #ifdef CONFIG_FEATURE_FUNKY + maybe_do_funky_stuff(bar, baz); + #endif + + Do this instead: + + (in .h header file) + + #if ENABLE_FEATURE_FUNKY + static inline void maybe_do_funky_stuff(int bar, int baz) + { + /* lotsa code in here */ + } + #else + static inline void maybe_do_funky_stuff(int bar, int baz) {} + #endif + + (in the .c source file) + + ret = my_func(bar, baz); + if (!ret) + return -1; + maybe_do_funky_stuff(bar, baz); + +The great thing about this approach is that the compiler will optimize away +the "no-op" case (the empty function) when the feature is turned off. + +Note also the use of the word 'maybe' in the function name to indicate +conditional execution. + + + +Notes on Strings +---------------- + +Strings in C can get a little thorny. Here's some guidelines for dealing with +strings in Busybox. (There is surely more that could be added to this +section.) + + +String Files +~~~~~~~~~~~~ + +Put all help/usage messages in usage.c. Put other strings in messages.c. +Putting these strings into their own file is a calculated decision designed to +confine spelling errors to a single place and aid internationalization +efforts, if needed. (Side Note: we might want to use a single file - maybe +called 'strings.c' - instead of two, food for thought). + + +Testing String Equivalence +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There's a right way and a wrong way to test for string equivalence with +strcmp(): + + The wrong way: + + if (!strcmp(string, "foo")) { + ... + + The right way: + + if (strcmp(string, "foo") == 0){ + ... + +The use of the "equals" (==) operator in the latter example makes it much more +obvious that you are testing for equivalence. The former example with the +"not" (!) operator makes it look like you are testing for an error. In a more +perfect world, we would have a streq() function in the string library, but +that ain't the world we're living in. + + +Avoid Dangerous String Functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unfortunately, the way C handles strings makes them prone to overruns when +certain library functions are (mis)used. The following table offers a summary +of some of the more notorious troublemakers: + +function overflows preferred +------------------------------------------------- +strcpy dest string safe_strncpy +strncpy may fail to 0-terminate dst safe_strncpy +strcat dest string strncat +gets string it gets fgets +getwd buf string getcwd +[v]sprintf str buffer [v]snprintf +realpath path buffer use with pathconf +[vf]scanf its arguments just avoid it + + +The above is by no means a complete list. Be careful out there. + + + +Avoid Big Static Buffers +------------------------ + +First, some background to put this discussion in context: static buffers look +like this in code: + + /* in a .c file outside any functions */ + static char buffer[BUFSIZ]; /* happily used by any function in this file, + but ick! big! */ + +The problem with these is that any time any busybox app is run, you pay a +memory penalty for this buffer, even if the applet that uses said buffer is +not run. This can be fixed, thusly: + + static char *buffer; + ... + other_func() + { + strcpy(buffer, lotsa_chars); /* happily uses global *buffer */ + ... + foo_main() + { + buffer = xmalloc(sizeof(char)*BUFSIZ); + ... + +However, this approach trades bss segment for text segment. Rather than +mallocing the buffers (and thus growing the text size), buffers can be +declared on the stack in the *_main() function and made available globally by +assigning them to a global pointer thusly: + + static char *pbuffer; + ... + other_func() + { + strcpy(pbuffer, lotsa_chars); /* happily uses global *pbuffer */ + ... + foo_main() + { + char *buffer[BUFSIZ]; /* declared locally, on stack */ + pbuffer = buffer; /* but available globally */ + ... + +This last approach has some advantages (low code size, space not used until +it's needed), but can be a problem in some low resource machines that have +very limited stack space (e.g., uCLinux). + +A macro is declared in busybox.h that implements compile-time selection +between xmalloc() and stack creation, so you can code the line in question as + + RESERVE_CONFIG_BUFFER(buffer, BUFSIZ); + +and the right thing will happen, based on your configuration. + +Another relatively new trick of similar nature is explained +in keep_data_small.txt. + + + +Miscellaneous Coding Guidelines +------------------------------- + +The following are important items that don't fit into any of the above +sections. + + +Model Busybox Applets After GNU Counterparts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When in doubt about the proper behavior of a Busybox program (output, +formatting, options, etc.), model it after the equivalent GNU program. +Doesn't matter how that program behaves on some other flavor of *NIX; doesn't +matter what the POSIX standard says or doesn't say, just model Busybox +programs after their GNU counterparts and it will make life easier on (nearly) +everyone. + +The only time we deviate from emulating the GNU behavior is when: + + - We are deliberately not supporting a feature (such as a command line + switch) + - Emulating the GNU behavior is prohibitively expensive (lots more code + would be required, lots more memory would be used, etc.) + - The difference is minor or cosmetic + +A note on the 'cosmetic' case: output differences might be considered +cosmetic, but if the output is significant enough to break other scripts that +use the output, it should really be fixed. + + +Scope +~~~~~ + +If a const variable is used only in a single source file, put it in the source +file and not in a header file. Likewise, if a const variable is used in only +one function, do not make it global to the file. Instead, declare it inside +the function body. Bottom line: Make a conscious effort to limit declarations +to the smallest scope possible. + +Inside applet files, all functions should be declared static so as to keep the +global name space clean. The only exception to this rule is the "applet_main" +function which must be declared extern. + +If you write a function that performs a task that could be useful outside the +immediate file, turn it into a general-purpose function with no ties to any +applet and put it in the utility.c file instead. + + +Brackets Are Your Friends +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Please use brackets on all if and else statements, even if it is only one +line. Example: + + Don't do this: + + if (foo) + stmt1; + stmt2 + stmt3; + + Do this instead: + + if (foo) { + stmt1; + } + stmt2 + stmt3; + +The "bracketless" approach is error prone because someday you might add a line +like this: + + if (foo) + stmt1; + new_line(); + stmt2; + stmt3; + +And the resulting behavior of your program would totally bewilder you. (Don't +laugh, it happens to us all.) Remember folks, this is C, not Python. + + +Function Declarations +~~~~~~~~~~~~~~~~~~~~~ + +Do not use old-style function declarations that declare variable types between +the parameter list and opening bracket. Example: + + Don't do this: + + int foo(parm1, parm2) + char parm1; + float parm2; + { + .... + + Do this instead: + + int foo(char parm1, float parm2) + { + .... + +The only time you would ever need to use the old declaration syntax is to +support ancient, antediluvian compilers. To our good fortune, we have access +to more modern compilers and the old declaration syntax is neither necessary +nor desired. + + +Emphasizing Logical Blocks +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Organization and readability are improved by putting extra newlines around +blocks of code that perform a single task. These are typically blocks that +begin with a C keyword, but not always. + +Furthermore, you should put a single comment (not necessarily one line, just +one comment) before the block, rather than commenting each and every line. +There is an optimal amount of commenting that a program can have; you can +comment too much as well as too little. + +A picture is really worth a thousand words here, the following example +illustrates how to emphasize logical blocks: + + while (line = xmalloc_fgets(fp)) { + + /* eat the newline, if any */ + chomp(line); + + /* ignore blank lines */ + if (strlen(file_to_act_on) == 0) { + continue; + } + + /* if the search string is in this line, print it, + * unless we were told to be quiet */ + if (strstr(line, search) && !be_quiet) { + puts(line); + } + + /* clean up */ + free(line); + } + + +Processing Options with getopt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your applet needs to process command-line switches, please use getopt32() to +do so. Numerous examples can be seen in many of the existing applets, but +basically it boils down to two things: at the top of the .c file, have this +line in the midst of your #includes, if you need to parse long options: + + #include + +Then have long options defined: + + static const char _longopts[] ALIGN1 = + "list\0" No_argument "t" + "extract\0" No_argument "x" + ; + +And a code block similar to the following near the top of your applet_main() +routine: + + char *str_b; + + opt_complementary = "cryptic_string"; + applet_long_options = _longopts; /* if you have them */ + opt = getopt32(argc, argv, "ab:c", &str_b); + if (opt & 1) { + handle_option_a(); + } + if (opt & 2) { + handle_option_b(str_b); + } + if (opt & 4) { + handle_option_c(); + } + +If your applet takes no options (such as 'init'), there should be a line +somewhere in the file reads: + + /* no options, no getopt */ + +That way, when people go grepping to see which applets need to be converted to +use getopt, they won't get false positives. + +For more info and examples, examine getopt32.c, tar.c, wget.c etc. diff --git a/busybox/docs/syslog.conf.txt b/busybox/docs/syslog.conf.txt new file mode 100644 index 00000000000000..6d9c4a173a6705 --- /dev/null +++ b/busybox/docs/syslog.conf.txt @@ -0,0 +1,28 @@ +If syslogd applet compiled with FEATURE_SYSLOGD_CFG=y, then it supports restricted syslog.conf. +The config resembles rsyslog.conf in RULES part: + +LINE = DELIM [RULE | COMMENT] +COMMENT = #.* +DELIM = SPACE TAB +RULE = SELECTOR [;SELECTOR]* DELIM* ACTION DELIM* +SELECTOR = FACILITY [,FACILITY]* .[[!]=] PRIORITY +FACILITY = * | kern | user ... (see syslog.h) +PRIORITY = * | emerg | alert ... (see syslog.h) +ACTION = FILE + +"mark" facility is NOT supported. +"none" priority is supported. +In FACILITY and PRIORITY "*" stands for "any". +FILE is a regular file or tty device. + +Here is an example: + +#syslog.conf +kern,user.* /var/log/messages #all messages of kern and user facilities +kern.!err /var/log/critical #all messages of kern facility with priorities lower than err (warn, notice ...) +*.*;auth,authpriv.none /var/log/noauth #all messages except ones with auth and authpriv facilities +kern,user.*;kern.!=notice;*.err;syslog.none /var/log/OMG #some whicked rule just as an example =) +*.* /dev/null #this prevents from logging to default log file (-O FILE or /var/log/messages) + +Even in the case of match with some rule another rules will be tried too. +If there was no match with any of the rules, logging to default log file or shared memory will be performed. diff --git a/busybox/docs/tar_pax.txt b/busybox/docs/tar_pax.txt new file mode 100644 index 00000000000000..e56c27b165ca46 --- /dev/null +++ b/busybox/docs/tar_pax.txt @@ -0,0 +1,239 @@ +'pax headers' is POSIX 2003 (iirc) addition designed to fix +tar format limitations - older tar format has fixed fields +for everything (filename, uid, filesize etc) which can overflow. + +pax Header Block + +The pax header block shall be identical to the ustar header block +described in ustar Interchange Format, except that two additional +typeflag values are defined: + +x + Represents extended header records for the following file in +the archive (which shall have its own ustar header block). + +g + Represents global extended header records for the following +files in the archive. Each value shall affect all subsequent files +that do not override that value in their own extended header +record and until another global extended header record is reached +that provides another value for the same field. The typeflag g +global headers should not be used with interchange media that +could suffer partial data loss in transporting the archive. + +For both of these types, the size field shall be the size of the +extended header records in octets. The other fields in the header +block are not meaningful to this version of the pax utility. +However, if this archive is read by a pax utility conforming to +the ISO POSIX-2:1993 standard, the header block fields are used to +create a regular file that contains the extended header records as +data. Therefore, header block field values should be selected to +provide reasonable file access to this regular file. + +A further difference from the ustar header block is that data +blocks for files of typeflag 1 (the digit one) (hard link) may be +included, which means that the size field may be greater than +zero. + +pax Extended Header + +An extended header shall consist of one or more records, each +constructed as follows: + +"%d %s=%s\n", , , + +The field shall be the decimal length of the extended +header record in octets, including length string itself and the +trailing . + +[skip] + +atime + The file access time for the following file(s), equivalent to +the value of the st_atime member of the stat structure for a file, +as described by the stat() function. The access time shall be +restored if the process has the appropriate privilege required to +do so. The format of the shall be as described in pax +Extended Header File Times. + +charset + The name of the character set used to encode the data in the +following file(s). + + The encoding is included in an extended header for information +only; when pax is used as described in IEEE Std 1003.1-2001, it +shall not translate the file data into any other encoding. The +BINARY entry indicates unencoded binary data. + + When used in write or copy mode, it is implementation-defined +whether pax includes a charset extended header record for a file. + +comment + A series of characters used as a comment. All characters in +the field shall be ignored by pax. + +gid + The group ID of the group that owns the file, expressed as a +decimal number using digits from the ISO/IEC 646:1991 standard. +This record shall override the gid field in the following header +block(s). When used in write or copy mode, pax shall include a gid +extended header record for each file whose group ID is greater +than 2097151 (octal 7777777). + +gname + The group of the file(s), formatted as a group name in the +group database. This record shall override the gid and gname +fields in the following header block(s), and any gid extended +header record. When used in read, copy, or list mode, pax shall +translate the name from the UTF-8 encoding in the header record to +the character set appropriate for the group database on the +receiving system. If any of the UTF-8 characters cannot be +translated, and if the -o invalid= UTF-8 option is not specified, +the results are implementation-defined. When used in write or copy +mode, pax shall include a gname extended header record for each +file whose group name cannot be represented entirely with the +letters and digits of the portable character set. + +linkpath + The pathname of a link being created to another file, of any +type, previously archived. This record shall override the linkname +field in the following ustar header block(s). The following ustar +header block shall determine the type of link created. If typeflag +of the following header block is 1, it shall be a hard link. If +typeflag is 2, it shall be a symbolic link and the linkpath value +shall be the contents of the symbolic link. The pax utility shall +translate the name of the link (contents of the symbolic link) +from the UTF-8 encoding to the character set appropriate for the +local file system. When used in write or copy mode, pax shall +include a linkpath extended header record for each link whose +pathname cannot be represented entirely with the members of the +portable character set other than NUL. + +mtime + The file modification time of the following file(s), +equivalent to the value of the st_mtime member of the stat +structure for a file, as described in the stat() function. This +record shall override the mtime field in the following header +block(s). The modification time shall be restored if the process +has the appropriate privilege required to do so. The format of the + shall be as described in pax Extended Header File Times. + +path + The pathname of the following file(s). This record shall +override the name and prefix fields in the following header +block(s). The pax utility shall translate the pathname of the file +from the UTF-8 encoding to the character set appropriate for the +local file system. + + When used in write or copy mode, pax shall include a path +extended header record for each file whose pathname cannot be +represented entirely with the members of the portable character +set other than NUL. + +realtime.any + The keywords prefixed by "realtime." are reserved for future +standardization. + +security.any + The keywords prefixed by "security." are reserved for future +standardization. + +size + The size of the file in octets, expressed as a decimal number +using digits from the ISO/IEC 646:1991 standard. This record shall +override the size field in the following header block(s). When +used in write or copy mode, pax shall include a size extended +header record for each file with a size value greater than +8589934591 (octal 77777777777). + +uid + The user ID of the file owner, expressed as a decimal number +using digits from the ISO/IEC 646:1991 standard. This record shall +override the uid field in the following header block(s). When used +in write or copy mode, pax shall include a uid extended header +record for each file whose owner ID is greater than 2097151 (octal +7777777). + +uname + The owner of the following file(s), formatted as a user name +in the user database. This record shall override the uid and uname +fields in the following header block(s), and any uid extended +header record. When used in read, copy, or list mode, pax shall +translate the name from the UTF-8 encoding in the header record to +the character set appropriate for the user database on the +receiving system. If any of the UTF-8 characters cannot be +translated, and if the -o invalid= UTF-8 option is not specified, +the results are implementation-defined. When used in write or copy +mode, pax shall include a uname extended header record for each +file whose user name cannot be represented entirely with the +letters and digits of the portable character set. + +If the field is zero length, it shall delete any header +block field, previously entered extended header value, or global +extended header value of the same name. + +If a keyword in an extended header record (or in a -o +option-argument) overrides or deletes a corresponding field in the +ustar header block, pax shall ignore the contents of that header +block field. + +Unlike the ustar header block fields, NULs shall not delimit +s; all characters within the field shall be +considered data for the field. None of the length limitations of +the ustar header block fields in ustar Header Block shall apply to +the extended header records. + +pax Extended Header File Times + +Time records shall be formatted as a decimal representation of the +time in seconds since the Epoch. If a period ( '.' ) decimal point +character is present, the digits to the right of the point shall +represent the units of a subsecond timing granularity. In read or +copy mode, the pax utility shall truncate the time of a file to +the greatest value that is not greater than the input header +file time. In write or copy mode, the pax utility shall output a +time exactly if it can be represented exactly as a decimal number, +and otherwise shall generate only enough digits so that the same +time shall be recovered if the file is extracted on a system whose +underlying implementation supports the same time granularity. + +Example from Linux kernel archive tarball: + +00000000 70 61 78 5f 67 6c 6f 62 61 6c 5f 68 65 61 64 65 |pax_global_heade| +00000010 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |r...............| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000060 00 00 00 00 30 30 30 30 36 36 36 00 30 30 30 30 |....0000666.0000| +00000070 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 |000.0000000.0000| +00000080 30 30 30 30 30 36 34 00 30 30 30 30 30 30 30 30 |0000064.00000000| +00000090 30 30 30 00 30 30 31 34 30 35 33 00 67 00 00 00 |000.0014053.g...| +000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000100 00 75 73 74 61 72 00 30 30 67 69 74 00 00 00 00 |.ustar.00git....| +00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000120 00 00 00 00 00 00 00 00 00 67 69 74 00 00 00 00 |.........git....| +00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000140 00 00 00 00 00 00 00 00 00 30 30 30 30 30 30 30 |.........0000000| +00000150 00 30 30 30 30 30 30 30 00 00 00 00 00 00 00 00 |.0000000........| +00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000200 35 32 20 63 6f 6d 6d 65 6e 74 3d 62 31 30 35 30 |52 comment=b1050| +00000210 32 62 32 32 61 31 32 30 39 64 36 62 34 37 36 33 |2b22a1209d6b4763| +00000220 39 64 38 38 62 38 31 32 62 32 31 66 62 35 39 34 |9d88b812b21fb594| +00000230 39 65 34 0a 00 00 00 00 00 00 00 00 00 00 00 00 |9e4.............| +00000240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +... diff --git a/busybox/docs/tcp.txt b/busybox/docs/tcp.txt new file mode 100644 index 00000000000000..2000f311004ff0 --- /dev/null +++ b/busybox/docs/tcp.txt @@ -0,0 +1,93 @@ + Some less-widely known details of TCP connections. + + Properly closing the connection. + +After this code sequence: + + sock = socket(AF_INET, SOCK_STREAM, 0); + connect(sock, &remote, sizeof(remote)); + write(sock, buffer, 1000000); + +a large block of data is only buffered by kernel, it can't be sent all at once. +What will happen if we close the socket? + +"A host MAY implement a 'half-duplex' TCP close sequence, so that + an application that has called close() cannot continue to read + data from the connection. If such a host issues a close() call + while received data is still pending in TCP, or if new data is + received after close() is called, its TCP SHOULD send a RST + to show that data was lost." + +IOW: if we just close(sock) now, kernel can reset the TCP connection +(send RST packet). + +This is problematic for two reasons: it discards some not-yet sent +data, and it may be reported as error, not EOF, on peer's side. + +What can be done about it? + +Solution #1: block until sending is done: + + /* When enabled, a close(2) or shutdown(2) will not return until + * all queued messages for the socket have been successfully sent + * or the linger timeout has been reached. + */ + struct linger { + int l_onoff; /* linger active */ + int l_linger; /* how many seconds to linger for */ + } linger; + linger.l_onoff = 1; + linger.l_linger = SOME_NUM; + setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); + close(sock); + +Solution #2: tell kernel that you are done sending. +This makes kernel send FIN after all data is written: + + shutdown(sock, SHUT_WR); + close(sock); + +However, experiments on Linux 3.9.4 show that kernel can return from +shutdown() and from close() before all data is sent, +and if peer sends any data to us after this, kernel still responds with +RST before all our data is sent. + +In practice the protocol in use often does not allow peer to send +such data to us, in which case this solution is acceptable. + +Solution #3: if you know that peer is going to close its end after it sees +our FIN (as EOF), it might be a good idea to perform a read after shutdown(). +When read finishes with 0-sized result, we conclude that peer received all +the data, saw EOF, and closed its end. + +However, this incurs small performance penalty (we run for a longer time) +and requires safeguards (nonblocking reads, timeouts etc) against +malicious peers which don't close the connection. + +Solutions #1 and #2 can be combined: + + /* ...set up struct linger... then: */ + setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); + shutdown(sock, SHUT_WR); + /* At this point, kernel sent FIN packet, not RST, to the peer, */ + /* even if there is buffered read data from the peer. */ + close(sock); + + Defeating Nagle. + +Method #1: manually control whether partial sends are allowed: + +This prevents partially filled packets being sent: + + int state = 1; + setsockopt(fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state)); + +and this forces last, partially filled packet (if any) to be sent: + + int state = 0; + setsockopt(fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state)); + +Method #2: make any write to immediately send data, even if it's partial: + + int state = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &state, sizeof(state)); diff --git a/busybox/docs/unicode.txt b/busybox/docs/unicode.txt new file mode 100644 index 00000000000000..9c159ce2a8c8ac --- /dev/null +++ b/busybox/docs/unicode.txt @@ -0,0 +1,71 @@ + Unicode support in busybox + +There are several scenarios where we need to handle unicode +correctly. + + Shell input + +We want to correctly handle input of unicode characters. +There are several problems with it. Just handling input +as sequence of bytes would break any editing. This was fixed +and now lineedit operates on the array of wchar_t's. +But we also need to handle the following problematic moments: + +* It is unreasonable to expect that output device supports + _any_ unicode chars. Perhaps we need to avoid printing + those chars which are not supported by output device. + Examples: chars which are not present in the font, + chars which are not assigned in unicode, + combining chars (especially trying to combine bad pairs: + a_chinese_symbol + "combining grave accent" = ??!) + +* We need to account for the fact that unicode chars have + different widths: 0 for combining chars, 1 for usual, + 2 for ideograms (are there 3+ wide chars?). + +* Bidirectional handling. If user wants to echo a phrase + in Hebrew, he types: echo "srettel werbeH" + + Editors (vi, ed) + +This case is a bit similar to "shell input", but unlike shell, +editors may encounter many more unexpected unicode sequences +(try to load a random binary file...), and they need to preserve +them, unlike shell which can afford to drop bogus input. + + more, less + +Need to correctly display any input file. Ideally, with +ASCII/unicode/filtered_unicode option or keyboard switch. +Note: need to handle tabs and backspaces specially +(bksp is for manpage compat). + + cut, fold, watch + +May need ability to cut unicode string to specified number of wchars +and/or to specified screen width. Need to handle tabs specially. + + sed, awk, grep + +Handle unicode-aware regexp match + + ls (multi-column display) + +ls will fail to line up columnar output if it will not account +for character widths (and maybe filter out some of them, see +above). OTOH, non-columnar views (ls -1, ls -l, ls | car) +should NOT filter out bad unicode (but need to filter out +control chars (coreutils does that). Note that unlike more/less, +tabs and backspaces need not special handling. + + top, ps + +Need to perform filtering similar to ls. + + Filename display (in error messages and elsewhere) + +Need to perform filtering similar to ls. + + +TODO: write an email to Asmus Freytag (asmus@unicode.org), +author of http://unicode.org/reports/tr11/ diff --git a/busybox/docs/unicode_UTF-8-test.txt b/busybox/docs/unicode_UTF-8-test.txt new file mode 100644 index 00000000000000..abd16f7253bf72 Binary files /dev/null and b/busybox/docs/unicode_UTF-8-test.txt differ diff --git a/busybox/docs/unicode_full-bmp.txt b/busybox/docs/unicode_full-bmp.txt new file mode 100644 index 00000000000000..2aeaa1e2ba2773 --- /dev/null +++ b/busybox/docs/unicode_full-bmp.txt @@ -0,0 +1,2079 @@ + +Full BMP Test File +------------------ + +Markus Kuhn -- 2003-04-22 + +This file contains the UTF-8 sequences of all code positions in the +ISO 10646-1 Basic Multilingual Plane, except for the C0 and C1 control +character areas. This corresponds to all codes in the range U+0020 - +U+007E and U+00A0 - U+FFFF. [uniset +0000..ffff utf8-list] + + +Basic Latin (U+0000-U+007F): + + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ +`abcdefghijklmnopqrstuvwxyz{|}~ + +Latin-1 Supplement (U+0080-U+00FF): + + ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß +àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ + +Latin Extended-A (U+0100-U+017F): + +ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿ +ŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ + +Latin Extended-B (U+0180-U+024F): + +ƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿ +ǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿ +ȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿ +ɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏ + +IPA Extensions (U+0250-U+02AF): + +ɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏ +ʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯ + +Spacing Modifier Letters (U+02B0-U+02FF): + +ʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿˀˁ˂˃˄˅ˆˇˈˉˊˋˌˍˎˏːˑ˒˓˔˕˖˗˘˙˚˛˜˝˞˟ˠˡˢˣˤ˥˦˧˨˩˪˫ˬ˭ˮ˯ +˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿ + +Combining Diacritical Marks (U+0300-U+036F): + +◌̀◌́◌̂◌̃◌̄◌̅◌̆◌̇◌̈◌̉◌̊◌̋◌̌◌̍◌̎◌̏◌̐◌̑◌̒◌̓◌̔◌̕◌̖◌̗◌̘◌̙◌̚◌̛◌̜◌̝◌̞◌̟◌̠◌̡◌̢◌̣◌̤◌̥◌̦◌̧◌̨◌̩◌̪◌̫◌̬◌̭◌̮◌̯◌̰◌̱◌̲◌̳◌̴◌̵◌̶◌̷◌̸◌̹◌̺◌̻◌̼◌̽◌̾◌̿ +◌̀◌́◌͂◌̓◌̈́◌ͅ◌͆◌͇◌͈◌͉◌͊◌͋◌͌◌͍◌͎◌͏͓͔͕͖͙͚͐͑͒͗͛͘͜͟͝͞◌͠◌͡◌͢◌ͣ◌ͤ◌ͥ◌ͦ◌ͧ◌ͨ◌ͩ◌ͪ◌ͫ◌ͬ◌ͭ◌ͮ◌ͯ + +Greek and Coptic (U+0370-U+03FF): + +ͰͱͲͳʹ͵Ͷͷ͸͹ͺͻͼͽ;Ϳ΀΁΂΃΄΅Ά·ΈΉΊ΋Ό΍ΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩΪΫάέήί +ΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώϏϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯ +ϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿ + +Cyrillic (U+0400-U+04FF): + +ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп +рстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿ +Ҁҁ҂◌҃◌҄◌҅◌҆҇ ҈ ҉ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿ +ӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿ + +Cyrillic Supplementary (U+0500-U+052F): + +ԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡԢԣԤԥԦԧԨԩԪԫԬԭԮԯ + +Armenian (U+0530-U+058F): + +԰ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖ՗՘ՙ՚՛՜՝՞՟ՠաբգդեզէըթժիլխծկ +հձղճմյնշոչպջռսվտրցւփքօֆևֈ։֊֋֌֍֎֏ + +Hebrew (U+0590-U+05FF): + +֐◌֑◌֒◌֓◌֔◌֕◌֖◌֗◌֘◌֙◌֚◌֛◌֜◌֝◌֞◌֟◌֠◌֢֡◌֣◌֤◌֥◌֦◌֧◌֨◌֩◌֪◌֫◌֬◌֭◌֮◌֯◌ְ◌ֱ◌ֲ◌ֳ◌ִ◌ֵ◌ֶ◌ַ◌ָ◌ֹֺ◌ֻ◌ּ◌ֽ־◌ֿ׀◌ׁ◌ׂ׃◌ׅׄ׆ׇ׈׉׊׋׌׍׎׏ +אבגדהוזחטיךכלםמןנסעףפץצקרשת׫׬׭׮ׯװױײ׳״׵׶׷׸׹׺׻׼׽׾׿ + +Arabic (U+0600-U+06FF): + +؀؁؂؃؄؅؆؇؈؉؊؋،؍؎؏ؘؙؚؐؑؒؓؔؕؖؗ؛؜؝؞؟ؠءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿ +ـفقكلمنهوىي◌ً◌ٌ◌ٍ◌َ◌ُ◌ِ◌ّ◌ْ◌ٓ◌ٔ◌ٕٖٜٟٗ٘ٙٚٛٝٞ٠١٢٣٤٥٦٧٨٩٪٫٬٭ٮٯ◌ٰٱٲٳٴٵٶٷٸٹٺٻټٽپٿ +ڀځڂڃڄڅچڇڈډڊڋڌڍڎڏڐڑڒړڔڕږڗژڙښڛڜڝڞڟڠڡڢڣڤڥڦڧڨکڪګڬڭڮگڰڱڲڳڴڵڶڷڸڹںڻڼڽھڿ +ۀہۂۃۄۅۆۇۈۉۊۋیۍێۏېۑےۓ۔ە◌ۖ◌ۗ◌ۘ◌ۙ◌ۚ◌ۛ◌ۜ۝ ۞◌۟◌۠◌ۡ◌ۢ◌ۣ◌ۤۥۦ◌ۧ◌ۨ۩◌۪◌۫◌۬◌ۭۮۯ۰۱۲۳۴۵۶۷۸۹ۺۻۼ۽۾ۿ + +Syriac (U+0700-U+074F): + +܀܁܂܃܄܅܆܇܈܉܊܋܌܍܎܏ܐ◌ܑܒܓܔܕܖܗܘܙܚܛܜܝܞܟܠܡܢܣܤܥܦܧܨܩܪܫܬܭܮܯ◌ܰ◌ܱ◌ܲ◌ܳ◌ܴ◌ܵ◌ܶ◌ܷ◌ܸ◌ܹ◌ܺ◌ܻ◌ܼ◌ܽ◌ܾ◌ܿ +◌݀◌݁◌݂◌݃◌݄◌݅◌݆◌݇◌݈◌݉◌݊݋݌ݍݎݏ + +Free block (U+0750-U+077F): + +ݐݑݒݓݔݕݖݗݘݙݚݛݜݝݞݟݠݡݢݣݤݥݦݧݨݩݪݫݬݭݮݯݰݱݲݳݴݵݶݷݸݹݺݻݼݽݾݿ + +Thaana (U+0780-U+07BF): + +ހށނރބޅކއވމފދތލގޏސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥ◌ަ◌ާ◌ި◌ީ◌ު◌ޫ◌ެ◌ޭ◌ޮ◌ޯ◌ްޱ޲޳޴޵޶޷޸޹޺޻޼޽޾޿ + +Free block (U+07C0-U+08FF): + +߀߁߂߃߄߅߆߇߈߉ߊߋߌߍߎߏߐߑߒߓߔߕߖߗߘߙߚߛߜߝߞߟߠߡߢߣߤߥߦߧߨߩߪ߲߫߬߭߮߯߰߱߳ߴߵ߶߷߸߹ߺ߻߼߽߾߿ +ࠀࠁࠂࠃࠄࠅࠆࠇࠈࠉࠊࠋࠌࠍࠎࠏࠐࠑࠒࠓࠔࠕࠖࠗ࠘࠙ࠚࠛࠜࠝࠞࠟࠠࠡࠢࠣࠤࠥࠦࠧࠨࠩࠪࠫࠬ࠭࠮࠯࠰࠱࠲࠳࠴࠵࠶࠷࠸࠹࠺࠻࠼࠽࠾࠿ +ࡀࡁࡂࡃࡄࡅࡆࡇࡈࡉࡊࡋࡌࡍࡎࡏࡐࡑࡒࡓࡔࡕࡖࡗࡘ࡙࡚࡛࡜࡝࡞࡟ࡠࡡࡢࡣࡤࡥࡦࡧࡨࡩࡪ࡫࡬࡭࡮࡯ࡰࡱࡲࡳࡴࡵࡶࡷࡸࡹࡺࡻࡼࡽࡾࡿ +ࢀࢁࢂࢃࢄࢅࢆࢇ࢈ࢉࢊࢋࢌࢍࢎ࢏࢐࢑࢒࢓࢔࢕࢖࢙࢚࢛ࢗ࢘࢜࢝࢞࢟ࢠࢡࢢࢣࢤࢥࢦࢧࢨࢩࢪࢫࢬࢭࢮࢯࢰࢱࢲࢳࢴࢵࢶࢷࢸࢹࢺࢻࢼࢽࢾࢿ +ࣀࣁࣂࣃࣄࣅࣆࣇࣈࣉ࣏࣐࣑࣒࣓࣊࣋࣌࣍࣎ࣔࣕࣖࣗࣘࣙࣚࣛࣜࣝࣞࣟ࣠࣡࣢ࣰࣱࣲࣣࣦࣩ࣭࣮࣯ࣶࣹࣺࣤࣥࣧࣨ࣪࣫࣬ࣳࣴࣵࣷࣸࣻࣼࣽࣾࣿ + +Devanagari (U+0900-U+097F): + +ऀ◌ँ◌ंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढणतथदधनऩपफबभमयरऱलळऴवशषसहऺऻ◌़ऽाि +ी◌ु◌ू◌ृ◌ॄ◌ॅ◌ॆ◌े◌ैॉॊोौ◌्ॎॏॐ◌॑◌॒◌॓◌॔ॕॖॗक़ख़ग़ज़ड़ढ़फ़य़ॠॡ◌ॢ◌ॣ।॥०१२३४५६७८९॰ॱॲॳॴॵॶॷॸॹॺॻॼॽॾॿ + +Bengali (U+0980-U+09FF): + +ঀ◌ঁংঃ঄অআইঈউঊঋঌ঍঎এঐ঑঒ওঔকখগঘঙচছজঝঞটঠডঢণতথদধন঩পফবভমযর঱ল঳঴঵শষসহ঺঻◌়ঽাি +ী◌ু◌ূ◌ৃ◌ৄ৅৆েৈ৉৊োৌ◌্ৎ৏৐৑৒৓৔৕৖ৗ৘৙৚৛ড়ঢ়৞য়ৠৡ◌ৢ◌ৣ৤৥০১২৩৪৫৬৭৮৯ৰৱ৲৳৴৵৶৷৸৹৺৻ৼ৽৾৿ + +Gurmukhi (U+0A00-U+0A7F): + +਀ਁ◌ਂਃ਄ਅਆਇਈਉਊ਋਌਍਎ਏਐ਑਒ਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨ਩ਪਫਬਭਮਯਰ਱ਲਲ਼਴ਵਸ਼਷ਸਹ਺਻◌਼਽ਾਿ +ੀ◌ੁ◌ੂ੃੄੅੆◌ੇ◌ੈ੉੊◌ੋ◌ੌ◌੍੎੏੐ੑ੒੓੔੕੖੗੘ਖ਼ਗ਼ਜ਼ੜ੝ਫ਼੟੠੡੢੣੤੥੦੧੨੩੪੫੬੭੮੯◌ੰ◌ੱੲੳੴੵ੶੷੸੹੺੻੼੽੾੿ + +Gujarati (U+0A80-U+0AFF): + +઀◌ઁ◌ંઃ઄અઆઇઈઉઊઋઌઍ઎એઐઑ઒ઓઔકખગઘઙચછજઝઞટઠડઢણતથદધન઩પફબભમયર઱લળ઴વશષસહ઺઻◌઼ઽાિ +ી◌ુ◌ૂ◌ૃ◌ૄ◌ૅ૆◌ે◌ૈૉ૊ોૌ◌્૎૏ૐ૑૒૓૔૕૖૗૘૙૚૛૜૝૞૟ૠૡૢૣ૤૥૦૧૨૩૪૫૬૭૮૯૰૱૲૳૴૵૶૷૸ૹૺૻૼ૽૾૿ + +Oriya (U+0B00-U+0B7F): + +଀◌ଁଂଃ଄ଅଆଇଈଉଊଋଌ଍଎ଏଐ଑଒ଓଔକଖଗଘଙଚଛଜଝଞଟଠଡଢଣତଥଦଧନ଩ପଫବଭମଯର଱ଲଳ଴ଵଶଷସହ଺଻◌଼ଽା◌ି +ୀ◌ୁ◌ୂ◌ୃୄ୅୆େୈ୉୊ୋୌ◌୍୎୏୐୑୒୓୔୕◌ୖୗ୘୙୚୛ଡ଼ଢ଼୞ୟୠୡୢୣ୤୥୦୧୨୩୪୫୬୭୮୯୰ୱ୲୳୴୵୶୷୸୹୺୻୼୽୾୿ + +Tamil (U+0B80-U+0BFF): + +஀஁◌ஂஃ஄அஆஇஈஉஊ஋஌஍எஏஐ஑ஒஓஔக஖஗஘ஙச஛ஜ஝ஞட஠஡஢ணத஥஦஧நனப஫஬஭மயரறலளழவஶஷஸஹ஺஻஼஽ாி +◌ீுூ௃௄௅ெேை௉ொோௌ◌்௎௏ௐ௑௒௓௔௕௖ௗ௘௙௚௛௜௝௞௟௠௡௢௣௤௥௦௧௨௩௪௫௬௭௮௯௰௱௲௳௴௵௶௷௸௹௺௻௼௽௾௿ + +Telugu (U+0C00-U+0C7F): + +ఀఁంఃఄఅఆఇఈఉఊఋఌ఍ఎఏఐ఑ఒఓఔకఖగఘఙచఛజఝఞటఠడఢణతథదధన఩పఫబభమయరఱలళఴవశషసహ఺఻఼ఽ◌ా◌ి +◌ీుూృౄ౅◌ె◌ే◌ై౉◌ొ◌ో◌ౌ◌్౎౏౐౑౒౓౔◌ౕ◌ౖ౗ౘౙౚ౛౜ౝ౞౟ౠౡౢౣ౤౥౦౧౨౩౪౫౬౭౮౯౰౱౲౳౴౵౶౷౸౹౺౻౼౽౾౿ + +Kannada (U+0C80-U+0CFF): + +ಀಁಂಃ಄ಅಆಇಈಉಊಋಌ಍ಎಏಐ಑ಒಓಔಕಖಗಘಙಚಛಜಝಞಟಠಡಢಣತಥದಧನ಩ಪಫಬಭಮಯರಱಲಳ಴ವಶಷಸಹ಺಻಼ಽಾ◌ಿ +ೀುೂೃೄ೅◌ೆೇೈ೉ೊೋ◌ೌ◌್೎೏೐೑೒೓೔ೕೖ೗೘೙೚೛೜ೝೞ೟ೠೡೢೣ೤೥೦೧೨೩೪೫೬೭೮೯೰ೱೲೳ೴೵೶೷೸೹೺೻೼೽೾೿ + +Malayalam (U+0D00-U+0D7F): + +ഀഁംഃഄഅആഇഈഉഊഋഌ഍എഏഐ഑ഒഓഔകഖഗഘങചഛജഝഞടഠഡഢണതഥദധനഩപഫബഭമയരറലളഴവശഷസഹഺ഻഼ഽാി +ീ◌ു◌ൂ◌ൃൄ൅െേൈ൉ൊോൌ◌്ൎ൏൐൑൒൓ൔൕൖൗ൘൙൚൛൜൝൞ൟൠൡൢൣ൤൥൦൧൨൩൪൫൬൭൮൯൰൱൲൳൴൵൶൷൸൹ൺൻർൽൾൿ + +Sinhala (U+0D80-U+0DFF): + +඀ඁංඃ඄අආඇඈඉඊඋඌඍඎඏඐඑඒඓඔඕඖ඗඘඙කඛගඝඞඟචඡජඣඤඥඦටඨඩඪණඬතථදධන඲ඳපඵබභමඹයර඼ල඾඿ +වශෂසහළෆ෇෈෉◌්෋෌෍෎ාැෑ◌ි◌ී◌ු෕◌ූ෗ෘෙේෛොෝෞෟ෠෡෢෣෤෥෦෧෨෩෪෫෬෭෮෯෰෱ෲෳ෴෵෶෷෸෹෺෻෼෽෾෿ + +Thai (U+0E00-U+0E7F): + +฀กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะ◌ัาำ◌ิ◌ี◌ึ◌ื◌ุ◌ู◌ฺ฻฼฽฾฿ +เแโใไๅๆ◌็◌่◌้◌๊◌๋◌์◌ํ◌๎๏๐๑๒๓๔๕๖๗๘๙๚๛๜๝๞๟๠๡๢๣๤๥๦๧๨๩๪๫๬๭๮๯๰๱๲๳๴๵๶๷๸๹๺๻๼๽๾๿ + +Lao (U+0E80-U+0EFF): + +຀ກຂ຃ຄ຅ຆງຈຉຊ຋ຌຍຎຏຐຑຒຓດຕຖທຘນບປຜຝພຟຠມຢຣ຤ລ຦ວຨຩສຫຬອຮຯະ◌ັາຳ◌ິ◌ີ◌ຶ◌ື◌ຸ◌຺ູ◌ົ◌ຼຽ຾຿ +ເແໂໃໄ໅ໆ໇◌່◌້◌໊◌໋◌໌◌ໍ໎໏໐໑໒໓໔໕໖໗໘໙໚໛ໜໝໞໟ໠໡໢໣໤໥໦໧໨໩໪໫໬໭໮໯໰໱໲໳໴໵໶໷໸໹໺໻໼໽໾໿ + +Tibetan (U+0F00-U+0FFF): + +ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗◌༘◌༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴◌༵༶◌༷༸◌༹༺༻༼༽༾༿ +ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰◌ཱ◌ི◌ཱི◌ུ◌ཱུ◌ྲྀ◌ཷ◌ླྀ◌ཹ◌ེ◌ཻ◌ོ◌ཽ◌ཾཿ +◌ྀ◌ཱྀ◌ྂ◌ྃ◌྄྅◌྆◌྇ྈྉྊྋྌྍྎྏ◌ྐ◌ྑ◌ྒ◌ྒྷ◌ྔ◌ྕ◌ྖ◌ྗ྘◌ྙ◌ྚ◌ྛ◌ྜ◌ྜྷ◌ྞ◌ྟ◌ྠ◌ྡ◌ྡྷ◌ྣ◌ྤ◌ྥ◌ྦ◌ྦྷ◌ྨ◌ྩ◌ྪ◌ྫ◌ྫྷ◌ྭ◌ྮ◌ྯ◌ྰ◌ྱ◌ྲ◌ླ◌ྴ◌ྵ◌ྶ◌ྷ◌ྸ◌ྐྵ◌ྺ◌ྻ◌ྼ྽྾྿ +࿀࿁࿂࿃࿄࿅◌࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚࿛࿜࿝࿞࿟࿠࿡࿢࿣࿤࿥࿦࿧࿨࿩࿪࿫࿬࿭࿮࿯࿰࿱࿲࿳࿴࿵࿶࿷࿸࿹࿺࿻࿼࿽࿾࿿ + +Myanmar (U+1000-U+109F): + +ကခဂဃငစဆဇဈဉညဋဌဍဎဏတထဒဓနပဖဗဘမယရလဝသဟဠအဢဣဤဥဦဧဨဩဪါာ◌ိ◌ီ◌ု◌ူေ◌ဲဳဴဵ◌ံ◌့း◌္်ျြွှဿ +၀၁၂၃၄၅၆၇၈၉၊။၌၍၎၏ၐၑၒၓၔၕၖၗ◌ၘ◌ၙၚၛၜၝၞၟၠၡၢၣၤၥၦၧၨၩၪၫၬၭၮၯၰၱၲၳၴၵၶၷၸၹၺၻၼၽၾၿ +ႀႁႂႃႄႅႆႇႈႉႊႋႌႍႎႏ႐႑႒႓႔႕႖႗႘႙ႚႛႜႝ႞႟ + +Georgian (U+10A0-U+10FF): + +ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ჆Ⴧ჈჉჊჋჌Ⴭ჎჏აბგდევზთიკლმნოპჟ +რსტუფქღყშჩცძწჭხჯჰჱჲჳჴჵჶჷჸჹჺ჻ჼჽჾჿ + +Hangul Jamo (U+1100-U+11FF): + +ᄀᄁᄂᄃᄄᄅᄆᄇᄈᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒᄓᄔᄕᄖᄗᄘᄙᄚᄛᄜᄝᄞᄟ +ᄠᄡᄢᄣᄤᄥᄦᄧᄨᄩᄪᄫᄬᄭᄮᄯᄰᄱᄲᄳᄴᄵᄶᄷᄸᄹᄺᄻᄼᄽᄾᄿ +ᅀᅁᅂᅃᅄᅅᅆᅇᅈᅉᅊᅋᅌᅍᅎᅏᅐᅑᅒᅓᅔᅕᅖᅗᅘᅙᅚᅛᅜᅝᅞᅟ +ᅠᅡᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵᅶᅷᅸᅹᅺᅻᅼᅽᅾᅿᆀᆁᆂᆃᆄᆅᆆᆇᆈᆉᆊᆋᆌᆍᆎᆏᆐᆑᆒᆓᆔᆕᆖᆗᆘᆙᆚᆛᆜᆝᆞᆟ +ᆠᆡᆢᆣᆤᆥᆦᆧᆨᆩᆪᆫᆬᆭᆮᆯᆰᆱᆲᆳᆴᆵᆶᆷᆸᆹᆺᆻᆼᆽᆾᆿᇀᇁᇂᇃᇄᇅᇆᇇᇈᇉᇊᇋᇌᇍᇎᇏᇐᇑᇒᇓᇔᇕᇖᇗᇘᇙᇚᇛᇜᇝᇞᇟ +ᇠᇡᇢᇣᇤᇥᇦᇧᇨᇩᇪᇫᇬᇭᇮᇯᇰᇱᇲᇳᇴᇵᇶᇷᇸᇹᇺᇻᇼᇽᇾᇿ + +Ethiopic (U+1200-U+137F): + +ሀሁሂሃሄህሆሇለሉሊላሌልሎሏሐሑሒሓሔሕሖሗመሙሚማሜምሞሟሠሡሢሣሤሥሦሧረሩሪራሬርሮሯሰሱሲሳሴስሶሷሸሹሺሻሼሽሾሿ +ቀቁቂቃቄቅቆቇቈ቉ቊቋቌቍ቎቏ቐቑቒቓቔቕቖ቗ቘ቙ቚቛቜቝ቞቟በቡቢባቤብቦቧቨቩቪቫቬቭቮቯተቱቲታቴትቶቷቸቹቺቻቼችቾቿ +ኀኁኂኃኄኅኆኇኈ኉ኊኋኌኍ኎኏ነኑኒናኔንኖኗኘኙኚኛኜኝኞኟአኡኢኣኤእኦኧከኩኪካኬክኮኯኰ኱ኲኳኴኵ኶኷ኸኹኺኻኼኽኾ኿ +ዀ዁ዂዃዄዅ዆዇ወዉዊዋዌውዎዏዐዑዒዓዔዕዖ዗ዘዙዚዛዜዝዞዟዠዡዢዣዤዥዦዧየዩዪያዬይዮዯደዱዲዳዴድዶዷዸዹዺዻዼዽዾዿ +ጀጁጂጃጄጅጆጇገጉጊጋጌግጎጏጐ጑ጒጓጔጕ጖጗ጘጙጚጛጜጝጞጟጠጡጢጣጤጥጦጧጨጩጪጫጬጭጮጯጰጱጲጳጴጵጶጷጸጹጺጻጼጽጾጿ +ፀፁፂፃፄፅፆፇፈፉፊፋፌፍፎፏፐፑፒፓፔፕፖፗፘፙፚ፛፜፝፞፟፠፡።፣፤፥፦፧፨፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼፽፾፿ + +Free block (U+1380-U+139F): + +ᎀᎁᎂᎃᎄᎅᎆᎇᎈᎉᎊᎋᎌᎍᎎᎏ᎐᎑᎒᎓᎔᎕᎖᎗᎘᎙᎚᎛᎜᎝᎞᎟ + +Cherokee (U+13A0-U+13FF): + +ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩᎪᎫᎬᎭᎮᎯᎰᎱᎲᎳᎴᎵᎶᎷᎸᎹᎺᎻᎼᎽᎾᎿᏀᏁᏂᏃᏄᏅᏆᏇᏈᏉᏊᏋᏌᏍᏎᏏᏐᏑᏒᏓᏔᏕᏖᏗᏘᏙᏚᏛᏜᏝᏞᏟ +ᏠᏡᏢᏣᏤᏥᏦᏧᏨᏩᏪᏫᏬᏭᏮᏯᏰᏱᏲᏳᏴᏵ᏶᏷ᏸᏹᏺᏻᏼᏽ᏾᏿ + +Unified Canadian Aboriginal Syllabics (U+1400-U+167F): + +᐀ᐁᐂᐃᐄᐅᐆᐇᐈᐉᐊᐋᐌᐍᐎᐏᐐᐑᐒᐓᐔᐕᐖᐗᐘᐙᐚᐛᐜᐝᐞᐟᐠᐡᐢᐣᐤᐥᐦᐧᐨᐩᐪᐫᐬᐭᐮᐯᐰᐱᐲᐳᐴᐵᐶᐷᐸᐹᐺᐻᐼᐽᐾᐿ +ᑀᑁᑂᑃᑄᑅᑆᑇᑈᑉᑊᑋᑌᑍᑎᑏᑐᑑᑒᑓᑔᑕᑖᑗᑘᑙᑚᑛᑜᑝᑞᑟᑠᑡᑢᑣᑤᑥᑦᑧᑨᑩᑪᑫᑬᑭᑮᑯᑰᑱᑲᑳᑴᑵᑶᑷᑸᑹᑺᑻᑼᑽᑾᑿ +ᒀᒁᒂᒃᒄᒅᒆᒇᒈᒉᒊᒋᒌᒍᒎᒏᒐᒑᒒᒓᒔᒕᒖᒗᒘᒙᒚᒛᒜᒝᒞᒟᒠᒡᒢᒣᒤᒥᒦᒧᒨᒩᒪᒫᒬᒭᒮᒯᒰᒱᒲᒳᒴᒵᒶᒷᒸᒹᒺᒻᒼᒽᒾᒿ +ᓀᓁᓂᓃᓄᓅᓆᓇᓈᓉᓊᓋᓌᓍᓎᓏᓐᓑᓒᓓᓔᓕᓖᓗᓘᓙᓚᓛᓜᓝᓞᓟᓠᓡᓢᓣᓤᓥᓦᓧᓨᓩᓪᓫᓬᓭᓮᓯᓰᓱᓲᓳᓴᓵᓶᓷᓸᓹᓺᓻᓼᓽᓾᓿ +ᔀᔁᔂᔃᔄᔅᔆᔇᔈᔉᔊᔋᔌᔍᔎᔏᔐᔑᔒᔓᔔᔕᔖᔗᔘᔙᔚᔛᔜᔝᔞᔟᔠᔡᔢᔣᔤᔥᔦᔧᔨᔩᔪᔫᔬᔭᔮᔯᔰᔱᔲᔳᔴᔵᔶᔷᔸᔹᔺᔻᔼᔽᔾᔿ +ᕀᕁᕂᕃᕄᕅᕆᕇᕈᕉᕊᕋᕌᕍᕎᕏᕐᕑᕒᕓᕔᕕᕖᕗᕘᕙᕚᕛᕜᕝᕞᕟᕠᕡᕢᕣᕤᕥᕦᕧᕨᕩᕪᕫᕬᕭᕮᕯᕰᕱᕲᕳᕴᕵᕶᕷᕸᕹᕺᕻᕼᕽᕾᕿ +ᖀᖁᖂᖃᖄᖅᖆᖇᖈᖉᖊᖋᖌᖍᖎᖏᖐᖑᖒᖓᖔᖕᖖᖗᖘᖙᖚᖛᖜᖝᖞᖟᖠᖡᖢᖣᖤᖥᖦᖧᖨᖩᖪᖫᖬᖭᖮᖯᖰᖱᖲᖳᖴᖵᖶᖷᖸᖹᖺᖻᖼᖽᖾᖿ +ᗀᗁᗂᗃᗄᗅᗆᗇᗈᗉᗊᗋᗌᗍᗎᗏᗐᗑᗒᗓᗔᗕᗖᗗᗘᗙᗚᗛᗜᗝᗞᗟᗠᗡᗢᗣᗤᗥᗦᗧᗨᗩᗪᗫᗬᗭᗮᗯᗰᗱᗲᗳᗴᗵᗶᗷᗸᗹᗺᗻᗼᗽᗾᗿ +ᘀᘁᘂᘃᘄᘅᘆᘇᘈᘉᘊᘋᘌᘍᘎᘏᘐᘑᘒᘓᘔᘕᘖᘗᘘᘙᘚᘛᘜᘝᘞᘟᘠᘡᘢᘣᘤᘥᘦᘧᘨᘩᘪᘫᘬᘭᘮᘯᘰᘱᘲᘳᘴᘵᘶᘷᘸᘹᘺᘻᘼᘽᘾᘿ +ᙀᙁᙂᙃᙄᙅᙆᙇᙈᙉᙊᙋᙌᙍᙎᙏᙐᙑᙒᙓᙔᙕᙖᙗᙘᙙᙚᙛᙜᙝᙞᙟᙠᙡᙢᙣᙤᙥᙦᙧᙨᙩᙪᙫᙬ᙭᙮ᙯᙰᙱᙲᙳᙴᙵᙶᙷᙸᙹᙺᙻᙼᙽᙾᙿ + +Ogham (U+1680-U+169F): + + ᚁᚂᚃᚄᚅᚆᚇᚈᚉᚊᚋᚌᚍᚎᚏᚐᚑᚒᚓᚔᚕᚖᚗᚘᚙᚚ᚛᚜᚝᚞᚟ + +Runic (U+16A0-U+16FF): + +ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀᛁᛂᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟ +ᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪ᛫᛬᛭ᛮᛯᛰᛱᛲᛳᛴᛵᛶᛷᛸ᛹᛺᛻᛼᛽᛾᛿ + +Tagalog (U+1700-U+171F): + +ᜀᜁᜂᜃᜄᜅᜆᜇᜈᜉᜊᜋᜌᜍᜎᜏᜐᜑ◌ᜒ◌ᜓ◌᜔᜕᜖᜗᜘᜙᜚᜛᜜᜝᜞ᜟ + +Hanunoo (U+1720-U+173F): + +ᜠᜡᜢᜣᜤᜥᜦᜧᜨᜩᜪᜫᜬᜭᜮᜯᜰᜱ◌ᜲ◌ᜳ◌᜴᜵᜶᜷᜸᜹᜺᜻᜼᜽᜾᜿ + +Buhid (U+1740-U+175F): + +ᝀᝁᝂᝃᝄᝅᝆᝇᝈᝉᝊᝋᝌᝍᝎᝏᝐᝑ◌ᝒ◌ᝓ᝔᝕᝖᝗᝘᝙᝚᝛᝜᝝᝞᝟ + +Tagbanwa (U+1760-U+177F): + +ᝠᝡᝢᝣᝤᝥᝦᝧᝨᝩᝪᝫᝬ᝭ᝮᝯᝰ᝱◌ᝲ◌ᝳ᝴᝵᝶᝷᝸᝹᝺᝻᝼᝽᝾᝿ + +Khmer (U+1780-U+17FF): + +កខគឃងចឆជឈញដឋឌឍណតថទធនបផពភមយរលវឝឞសហឡអឣឤឥឦឧឨឩឪឫឬឭឮឯឰឱឲឳ឴឵ា◌ិ◌ី◌ឹ◌ឺ◌ុ◌ូ◌ួើឿ +ៀេែៃោៅ◌ំះៈ◌៉◌៊◌់◌៌◌៍◌៎◌៏◌័◌៑◌្◌៓។៕៖ៗ៘៙៚៛ៜ៝៞៟០១២៣៤៥៦៧៨៩៪៫៬៭៮៯៰៱៲៳៴៵៶៷៸៹៺៻៼៽៾៿ + +Mongolian (U+1800-U+18AF): + +᠀᠁᠂᠃᠄᠅᠆᠇᠈᠉᠊◌᠋◌᠌◌᠍᠎᠏᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙᠚᠛᠜᠝᠞᠟ᠠᠡᠢᠣᠤᠥᠦᠧᠨᠩᠪᠫᠬᠭᠮᠯᠰᠱᠲᠳᠴᠵᠶᠷᠸᠹᠺᠻᠼᠽᠾᠿ +ᡀᡁᡂᡃᡄᡅᡆᡇᡈᡉᡊᡋᡌᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡘᡙᡚᡛᡜᡝᡞᡟᡠᡡᡢᡣᡤᡥᡦᡧᡨᡩᡪᡫᡬᡭᡮᡯᡰᡱᡲᡳᡴᡵᡶᡷᡸ᡹᡺᡻᡼᡽᡾᡿ +ᢀᢁᢂᢃᢄᢅᢆᢇᢈᢉᢊᢋᢌᢍᢎᢏᢐᢑᢒᢓᢔᢕᢖᢗᢘᢙᢚᢛᢜᢝᢞᢟᢠᢡᢢᢣᢤᢥᢦᢧᢨ◌ᢩᢪ᢫᢬᢭᢮᢯ + +Free block (U+18B0-U+18FF): + +ᢰᢱᢲᢳᢴᢵᢶᢷᢸᢹᢺᢻᢼᢽᢾᢿᣀᣁᣂᣃᣄᣅᣆᣇᣈᣉᣊᣋᣌᣍᣎᣏᣐᣑᣒᣓᣔᣕᣖᣗᣘᣙᣚᣛᣜᣝᣞᣟᣠᣡᣢᣣᣤᣥᣦᣧᣨᣩᣪᣫᣬᣭᣮᣯ +ᣰᣱᣲᣳᣴᣵ᣶᣷᣸᣹᣺᣻᣼᣽᣾᣿ + +Limbu (U+1900-U+194F): + +ᤀᤁᤂᤃᤄᤅᤆᤇᤈᤉᤊᤋᤌᤍᤎᤏᤐᤑᤒᤓᤔᤕᤖᤗᤘᤙᤚᤛᤜᤝᤞ᤟ᤠᤡᤢᤣᤤᤥᤦᤧᤨᤩᤪᤫ᤬᤭᤮᤯ᤰᤱᤲᤳᤴᤵᤶᤷᤸ᤻᤹᤺᤼᤽᤾᤿ +᥀᥁᥂᥃᥄᥅᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏ + +Tai Le (U+1950-U+197F): + +ᥐᥑᥒᥓᥔᥕᥖᥗᥘᥙᥚᥛᥜᥝᥞᥟᥠᥡᥢᥣᥤᥥᥦᥧᥨᥩᥪᥫᥬᥭ᥮᥯ᥰᥱᥲᥳᥴ᥵᥶᥷᥸᥹᥺᥻᥼᥽᥾᥿ + +Free block (U+1980-U+19DF): + +ᦀᦁᦂᦃᦄᦅᦆᦇᦈᦉᦊᦋᦌᦍᦎᦏᦐᦑᦒᦓᦔᦕᦖᦗᦘᦙᦚᦛᦜᦝᦞᦟᦠᦡᦢᦣᦤᦥᦦᦧᦨᦩᦪᦫ᦬᦭᦮᦯ᦰᦱᦲᦳᦴᦵᦶᦷᦸᦹᦺᦻᦼᦽᦾᦿ +ᧀᧁᧂᧃᧄᧅᧆᧇᧈᧉ᧊᧋᧌᧍᧎᧏᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᧚᧛᧜᧝᧞᧟ + +Khmer Symbols (U+19E0-U+19FF): + +᧠᧡᧢᧣᧤᧥᧦᧧᧨᧩᧪᧫᧬᧭᧮᧯᧰᧱᧲᧳᧴᧵᧶᧷᧸᧹᧺᧻᧼᧽᧾᧿ + +Free block (U+1A00-U+1CFF): + +ᨀᨁᨂᨃᨄᨅᨆᨇᨈᨉᨊᨋᨌᨍᨎᨏᨐᨑᨒᨓᨔᨕᨖᨘᨗᨙᨚᨛ᨜᨝᨞᨟ᨠᨡᨢᨣᨤᨥᨦᨧᨨᨩᨪᨫᨬᨭᨮᨯᨰᨱᨲᨳᨴᨵᨶᨷᨸᨹᨺᨻᨼᨽᨾᨿ +ᩀᩁᩂᩃᩄᩅᩆᩇᩈᩉᩊᩋᩌᩍᩎᩏᩐᩑᩒᩓᩔᩕᩖᩗᩘᩙᩚᩛᩜᩝᩞ᩟᩠ᩡᩢᩣᩤᩥᩦᩧᩨᩩᩪᩫᩬᩭᩮᩯᩰᩱᩲᩳᩴ᩵᩶᩷᩸᩹᩺᩻᩼᩽᩾᩿ +᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪊᪋᪌᪍᪎᪏᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙᪚᪛᪜᪝᪞᪟᪠᪡᪢᪣᪤᪥᪦ᪧ᪨᪩᪪᪫᪬᪭᪮᪯᪵᪶᪷᪸᪹᪺᪽᪰᪱᪲᪳᪴᪻᪼᪾ᪿ +ᫀ᫃᫄᫊᫁᫂᫅᫆᫇᫈᫉᫋ᫌᫍᫎ᫏᫐᫑᫒᫓᫔᫕᫖᫗᫘᫙᫚᫛᫜᫝᫞᫟᫠᫡᫢᫣᫤᫥᫦᫧᫨᫩᫪᫫᫬᫭᫮᫯᫰᫱᫲᫳᫴᫵᫶᫷᫸᫹᫺᫻᫼᫽᫾᫿ +ᬀᬁᬂᬃᬄᬅᬆᬇᬈᬉᬊᬋᬌᬍᬎᬏᬐᬑᬒᬓᬔᬕᬖᬗᬘᬙᬚᬛᬜᬝᬞᬟᬠᬡᬢᬣᬤᬥᬦᬧᬨᬩᬪᬫᬬᬭᬮᬯᬰᬱᬲᬳ᬴ᬵᬶᬷᬸᬹᬺᬻᬼᬽᬾᬿ +ᭀᭁᭂᭃ᭄ᭅᭆᭇᭈᭉᭊᭋᭌ᭍᭎᭏᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᭚᭛᭜᭝᭞᭟᭠᭡᭢᭣᭤᭥᭦᭧᭨᭩᭪᭬᭫᭭᭮᭯᭰᭱᭲᭳᭴᭵᭶᭷᭸᭹᭺᭻᭼᭽᭾᭿ +ᮀᮁᮂᮃᮄᮅᮆᮇᮈᮉᮊᮋᮌᮍᮎᮏᮐᮑᮒᮓᮔᮕᮖᮗᮘᮙᮚᮛᮜᮝᮞᮟᮠᮡᮢᮣᮤᮥᮦᮧᮨᮩ᮪᮫ᮬᮭᮮᮯ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹ᮺᮻᮼᮽᮾᮿ +ᯀᯁᯂᯃᯄᯅᯆᯇᯈᯉᯊᯋᯌᯍᯎᯏᯐᯑᯒᯓᯔᯕᯖᯗᯘᯙᯚᯛᯜᯝᯞᯟᯠᯡᯢᯣᯤᯥ᯦ᯧᯨᯩᯪᯫᯬᯭᯮᯯᯰᯱ᯲᯳᯴᯵᯶᯷᯸᯹᯺᯻᯼᯽᯾᯿ +ᰀᰁᰂᰃᰄᰅᰆᰇᰈᰉᰊᰋᰌᰍᰎᰏᰐᰑᰒᰓᰔᰕᰖᰗᰘᰙᰚᰛᰜᰝᰞᰟᰠᰡᰢᰣᰤᰥᰦᰧᰨᰩᰪᰫᰬᰭᰮᰯᰰᰱᰲᰳᰴᰵᰶ᰷᰸᰹᰺᰻᰼᰽᰾᰿ +᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱊᱋᱌ᱍᱎᱏ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙ᱚᱛᱜᱝᱞᱟᱠᱡᱢᱣᱤᱥᱦᱧᱨᱩᱪᱫᱬᱭᱮᱯᱰᱱᱲᱳᱴᱵᱶᱷᱸᱹᱺᱻᱼᱽ᱾᱿ +ᲀᲁᲂᲃᲄᲅᲆᲇᲈᲉᲊ᲋᲌᲍᲎᲏ᲐᲑᲒᲓᲔᲕᲖᲗᲘᲙᲚᲛᲜᲝᲞᲟᲠᲡᲢᲣᲤᲥᲦᲧᲨᲩᲪᲫᲬᲭᲮᲯᲰᲱᲲᲳᲴᲵᲶᲷᲸᲹᲺ᲻᲼ᲽᲾᲿ +᳀᳁᳂᳃᳄᳅᳆᳇᳈᳉᳊᳋᳌᳍᳎᳏᳐᳑᳒᳓᳔᳕᳖᳗᳘᳙᳜᳝᳞᳟᳚᳛᳠᳡᳢᳣᳤᳥᳦᳧᳨ᳩᳪᳫᳬ᳭ᳮᳯᳰᳱᳲᳳ᳴ᳵᳶ᳷᳸᳹ᳺ᳻᳼᳽᳾᳿ + +Phonetic Extensions (U+1D00-U+1D7F): + +ᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩᴪᴫᴬᴭᴮᴯᴰᴱᴲᴳᴴᴵᴶᴷᴸᴹᴺᴻᴼᴽᴾᴿ +ᵀᵁᵂᵃᵄᵅᵆᵇᵈᵉᵊᵋᵌᵍᵎᵏᵐᵑᵒᵓᵔᵕᵖᵗᵘᵙᵚᵛᵜᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵸᵹᵺᵻᵼᵽᵾᵿ + +Free block (U+1D80-U+1DFF): + +ᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚᶛᶜᶝᶞᶟᶠᶡᶢᶣᶤᶥᶦᶧᶨᶩᶪᶫᶬᶭᶮᶯᶰᶱᶲᶳᶴᶵᶶᶷᶸᶹᶺᶻᶼᶽᶾᶿ +᷐᷎᷺᷂᷊᷏᷹᷽᷿᷷᷸᷀᷁᷃᷄᷅᷆᷇᷈᷉᷋᷌᷑᷒ᷓᷔᷕᷖᷗᷘᷙᷚᷛᷜᷝᷞᷟᷠᷡᷢᷣᷤᷥᷦᷧᷨᷩᷪᷫᷬᷭᷮᷯᷰᷱᷲᷳᷴ᷵᷻᷾᷶᷼᷍ + +Latin Extended Additional (U+1E00-U+1EFF): + +ḀḁḂḃḄḅḆḇḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿ +ṀṁṂṃṄṅṆṇṈṉṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿ +ẀẁẂẃẄẅẆẇẈẉẊẋẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẜẝẞẟẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾế +ỀềỂểỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹỺỻỼỽỾỿ + +Greek Extended (U+1F00-U+1FFF): + +ἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕ἖἗ἘἙἚἛἜἝ἞἟ἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿ +ὀὁὂὃὄὅ὆὇ὈὉὊὋὌὍ὎὏ὐὑὒὓὔὕὖὗ὘Ὑ὚Ὓ὜Ὕ὞ὟὠὡὢὣὤὥὦὧὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώ὾὿ +ᾀᾁᾂᾃᾄᾅᾆᾇᾈᾉᾊᾋᾌᾍᾎᾏᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾞᾟᾠᾡᾢᾣᾤᾥᾦᾧᾨᾩᾪᾫᾬᾭᾮᾯᾰᾱᾲᾳᾴ᾵ᾶᾷᾸᾹᾺΆᾼ᾽ι᾿ +῀῁ῂῃῄ῅ῆῇῈΈῊΉῌ῍῎῏ῐῑῒΐ῔῕ῖῗῘῙῚΊ῜῝῞῟ῠῡῢΰῤῥῦῧῨῩῪΎῬ῭΅`῰῱ῲῳῴ῵ῶῷῸΌῺΏῼ´῾῿ + +General Punctuation (U+2000-U+206F): + +           ​‌‍‎‏‐‑‒–—―‖‗‘’‚‛“”„‟†‡•‣․‥…‧

‪‫‬‭‮ ‰‱′″‴‵‶‷‸‹›※‼‽‾‿ +⁀⁁⁂⁃⁄⁅⁆⁇⁈⁉⁊⁋⁌⁍⁎⁏⁐⁑⁒⁓⁔⁕⁖⁗⁘⁙⁚⁛⁜⁝⁞ ⁠⁡⁢⁣⁤⁥⁦⁧⁨⁩ + +Superscripts and Subscripts (U+2070-U+209F): + +⁰ⁱ⁲⁳⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎₏ₐₑₒₓₔₕₖₗₘₙₚₛₜ₝₞₟ + +Currency Symbols (U+20A0-U+20CF): + +₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿⃀⃁⃂⃃⃄⃅⃆⃇⃈⃉⃊⃋⃌⃍⃎⃏ + +Combining Diacritical Marks for Symbols (U+20D0-U+20FF): + +◌⃐◌⃑◌⃒◌⃓◌⃔◌⃕◌⃖◌⃗◌⃘◌⃙◌⃚◌⃛◌⃜ ⃝ ⃞ ⃟ ⃠◌⃡ ⃢ ⃣ ⃤◌⃥◌⃦◌⃧◌⃨◌⃩◌⃪⃫⃬⃭⃮⃯⃰⃱⃲⃳⃴⃵⃶⃷⃸⃹⃺⃻⃼⃽⃾⃿ + +Letterlike Symbols (U+2100-U+214F): + +℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠℡™℣ℤ℥Ω℧ℨ℩KÅℬℭ℮ℯℰℱℲℳℴℵℶℷℸℹ℺℻ℼℽℾℿ +⅀⅁⅂⅃⅄ⅅⅆⅇⅈⅉ⅊⅋⅌⅍ⅎ⅏ + +Number Forms (U+2150-U+218F): + +⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↃↄↅↆↇↈ↉↊↋↌↍↎↏ + +Arrows (U+2190-U+21FF): + +←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏ +⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇫⇬⇭⇮⇯⇰⇱⇲⇳⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿ + +Mathematical Operators (U+2200-U+22FF): + +∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟∠∡∢∣∤∥∦∧∨∩∪∫∬∭∮∯∰∱∲∳∴∵∶∷∸∹∺∻∼∽∾∿ +≀≁≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺≻≼≽≾≿ +⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌⊍⊎⊏⊐⊑⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜⊝⊞⊟⊠⊡⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯⊰⊱⊲⊳⊴⊵⊶⊷⊸⊹⊺⊻⊼⊽⊾⊿ +⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭⋮⋯⋰⋱⋲⋳⋴⋵⋶⋷⋸⋹⋺⋻⋼⋽⋾⋿ + +Miscellaneous Technical (U+2300-U+23FF): + +⌀⌁⌂⌃⌄⌅⌆⌇⌈⌉⌊⌋⌌⌍⌎⌏⌐⌑⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌜⌝⌞⌟⌠⌡⌢⌣⌤⌥⌦⌧⌨〈〉⌫⌬⌭⌮⌯⌰⌱⌲⌳⌴⌵⌶⌷⌸⌹⌺⌻⌼⌽ +⌾⌿⍀⍁⍂⍃⍄⍅⍆⍇⍈⍉⍊⍋⍌⍍⍎⍏⍐⍑⍒⍓⍔⍕⍖⍗⍘⍙⍚⍛⍜⍝⍞⍟⍠⍡⍢⍣⍤⍥⍦⍧⍨⍩⍪⍫⍬⍭⍮⍯⍰⍱⍲⍳⍴⍵⍶⍷⍸⍹⍺⍻⍼⍽ +⍾⍿⎀⎁⎂⎃⎄⎅⎆⎇⎈⎉⎊⎋⎌⎍⎎⎏⎐⎑⎒⎓⎔⎕⎖⎗⎘⎙⎚⎛⎜⎝⎞⎟⎠⎡⎢⎣⎤⎥⎦⎧⎨⎩⎪⎫⎬⎭⎮⎯⎰⎱⎲⎳⎴⎵⎶⎷⎸⎹⎺⎻⎼⎽ +⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇⏈⏉⏊⏋⏌⏍⏎⏏⏐⏑⏒⏓⏔⏕⏖⏗⏘⏙⏚⏛⏜⏝⏞⏟⏠⏡⏢⏣⏤⏥⏦⏧⏨⏩⏪⏫⏬⏭⏮⏯⏰⏱⏲⏳⏴⏵⏶⏷⏸⏹⏺⏻⏼⏽ +⏾⏿ + +Control Pictures (U+2400-U+243F): + +␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟␠␡␢␣␤␥␦␧␨␩␪␫␬␭␮␯␰␱␲␳␴␵␶␷␸␹␺␻␼␽␾␿ + +Optical Character Recognition (U+2440-U+245F): + +⑀⑁⑂⑃⑄⑅⑆⑇⑈⑉⑊⑋⑌⑍⑎⑏⑐⑑⑒⑓⑔⑕⑖⑗⑘⑙⑚⑛⑜⑝⑞⑟ + +Enclosed Alphanumerics (U+2460-U+24FF): + +①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⒜⒝⒞⒟ +⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟ +ⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿ + +Box Drawing (U+2500-U+257F): + +─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿ +╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿ + +Block Elements (U+2580-U+259F): + +▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟ + +Geometric Shapes (U+25A0-U+25FF): + +■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟ +◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿ + +Miscellaneous Symbols (U+2600-U+26FF): + +☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿ +♀♁♂♃♄♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿ +⚀⚁⚂⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌⚍⚎⚏⚐⚑⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜⚝⚞⚟⚠⚡⚢⚣⚤⚥⚦⚧⚨⚩⚪⚫⚬⚭⚮⚯⚰⚱⚲⚳⚴⚵⚶⚷⚸⚹⚺⚻⚼⚽⚾⚿ +⛀⛁⛂⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌⛍⛎⛏⛐⛑⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜⛝⛞⛟⛠⛡⛢⛣⛤⛥⛦⛧⛨⛩⛪⛫⛬⛭⛮⛯⛰⛱⛲⛳⛴⛵⛶⛷⛸⛹⛺⛻⛼⛽⛾⛿ + +Dingbats (U+2700-U+27BF): + +✀✁✂✃✄✅✆✇✈✉✊✋✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟✠✡✢✣✤✥✦✧✨✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿ +❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞❟❠❡❢❣❤❥❦❧❨❩❪❫❬❭❮❯❰❱❲❳❴❵❶❷❸❹❺❻❼❽❾❿ +➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓➔➕➖➗➘➙➚➛➜➝➞➟➠➡➢➣➤➥➦➧➨➩➪➫➬➭➮➯➰➱➲➳➴➵➶➷➸➹➺➻➼➽➾➿ + +Miscellaneous Mathematical Symbols-A (U+27C0-U+27EF): + +⟀⟁⟂⟃⟄⟅⟆⟇⟈⟉⟊⟋⟌⟍⟎⟏⟐⟑⟒⟓⟔⟕⟖⟗⟘⟙⟚⟛⟜⟝⟞⟟⟠⟡⟢⟣⟤⟥⟦⟧⟨⟩⟪⟫⟬⟭⟮⟯ + +Supplemental Arrows-A (U+27F0-U+27FF): + +⟰⟱⟲⟳⟴⟵⟶⟷⟸⟹⟺⟻⟼⟽⟾⟿ + +Braille Patterns (U+2800-U+28FF): + +⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿ +⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿ +⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿ +⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿ + +Supplemental Arrows-B (U+2900-U+297F): + +⤀⤁⤂⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌⤍⤎⤏⤐⤑⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜⤝⤞⤟⤠⤡⤢⤣⤤⤥⤦⤧⤨⤩⤪⤫⤬⤭⤮⤯⤰⤱⤲⤳⤴⤵⤶⤷⤸⤹⤺⤻⤼⤽⤾⤿ +⥀⥁⥂⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌⥍⥎⥏⥐⥑⥒⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜⥝⥞⥟⥠⥡⥢⥣⥤⥥⥦⥧⥨⥩⥪⥫⥬⥭⥮⥯⥰⥱⥲⥳⥴⥵⥶⥷⥸⥹⥺⥻⥼⥽⥾⥿ + +Miscellaneous Mathematical Symbols-B (U+2980-U+29FF): + +⦀⦁⦂⦃⦄⦅⦆⦇⦈⦉⦊⦋⦌⦍⦎⦏⦐⦑⦒⦓⦔⦕⦖⦗⦘⦙⦚⦛⦜⦝⦞⦟⦠⦡⦢⦣⦤⦥⦦⦧⦨⦩⦪⦫⦬⦭⦮⦯⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿ +⧀⧁⧂⧃⧄⧅⧆⧇⧈⧉⧊⧋⧌⧍⧎⧏⧐⧑⧒⧓⧔⧕⧖⧗⧘⧙⧚⧛⧜⧝⧞⧟⧠⧡⧢⧣⧤⧥⧦⧧⧨⧩⧪⧫⧬⧭⧮⧯⧰⧱⧲⧳⧴⧵⧶⧷⧸⧹⧺⧻⧼⧽⧾⧿ + +Supplemental Mathematical Operators (U+2A00-U+2AFF): + +⨀⨁⨂⨃⨄⨅⨆⨇⨈⨉⨊⨋⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜⨝⨞⨟⨠⨡⨢⨣⨤⨥⨦⨧⨨⨩⨪⨫⨬⨭⨮⨯⨰⨱⨲⨳⨴⨵⨶⨷⨸⨹⨺⨻⨼⨽⨾⨿ +⩀⩁⩂⩃⩄⩅⩆⩇⩈⩉⩊⩋⩌⩍⩎⩏⩐⩑⩒⩓⩔⩕⩖⩗⩘⩙⩚⩛⩜⩝⩞⩟⩠⩡⩢⩣⩤⩥⩦⩧⩨⩩⩪⩫⩬⩭⩮⩯⩰⩱⩲⩳⩴⩵⩶⩷⩸⩹⩺⩻⩼⩽⩾⩿ +⪀⪁⪂⪃⪄⪅⪆⪇⪈⪉⪊⪋⪌⪍⪎⪏⪐⪑⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜⪝⪞⪟⪠⪡⪢⪣⪤⪥⪦⪧⪨⪩⪪⪫⪬⪭⪮⪯⪰⪱⪲⪳⪴⪵⪶⪷⪸⪹⪺⪻⪼⪽⪾⪿ +⫀⫁⫂⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌⫍⫎⫏⫐⫑⫒⫓⫔⫕⫖⫗⫘⫙⫚⫛⫝̸⫝⫞⫟⫠⫡⫢⫣⫤⫥⫦⫧⫨⫩⫪⫫⫬⫭⫮⫯⫰⫱⫲⫳⫴⫵⫶⫷⫸⫹⫺⫻⫼⫽⫾⫿ + +Miscellaneous Symbols and Arrows (U+2B00-U+2BFF): + +⬀⬁⬂⬃⬄⬅⬆⬇⬈⬉⬊⬋⬌⬍⬎⬏⬐⬑⬒⬓⬔⬕⬖⬗⬘⬙⬚⬛⬜⬝⬞⬟⬠⬡⬢⬣⬤⬥⬦⬧⬨⬩⬪⬫⬬⬭⬮⬯⬰⬱⬲⬳⬴⬵⬶⬷⬸⬹⬺⬻⬼⬽⬾⬿ +⭀⭁⭂⭃⭄⭅⭆⭇⭈⭉⭊⭋⭌⭍⭎⭏⭐⭑⭒⭓⭔⭕⭖⭗⭘⭙⭚⭛⭜⭝⭞⭟⭠⭡⭢⭣⭤⭥⭦⭧⭨⭩⭪⭫⭬⭭⭮⭯⭰⭱⭲⭳⭴⭵⭶⭷⭸⭹⭺⭻⭼⭽⭾⭿ +⮀⮁⮂⮃⮄⮅⮆⮇⮈⮉⮊⮋⮌⮍⮎⮏⮐⮑⮒⮓⮔⮕⮖⮗⮘⮙⮚⮛⮜⮝⮞⮟⮠⮡⮢⮣⮤⮥⮦⮧⮨⮩⮪⮫⮬⮭⮮⮯⮰⮱⮲⮳⮴⮵⮶⮷⮸⮹⮺⮻⮼⮽⮾⮿ +⯀⯁⯂⯃⯄⯅⯆⯇⯈⯉⯊⯋⯌⯍⯎⯏⯐⯑⯒⯓⯔⯕⯖⯗⯘⯙⯚⯛⯜⯝⯞⯟⯠⯡⯢⯣⯤⯥⯦⯧⯨⯩⯪⯫⯬⯭⯮⯯⯰⯱⯲⯳⯴⯵⯶⯷⯸⯹⯺⯻⯼⯽⯾⯿ + +Free block (U+2C00-U+2E7F): + +ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮⰯⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿ +ⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞⱟⱠⱡⱢⱣⱤⱥⱦⱧⱨⱩⱪⱫⱬⱭⱮⱯⱰⱱⱲⱳⱴⱵⱶⱷⱸⱹⱺⱻⱼⱽⱾⱿ +ⲀⲁⲂⲃⲄⲅⲆⲇⲈⲉⲊⲋⲌⲍⲎⲏⲐⲑⲒⲓⲔⲕⲖⲗⲘⲙⲚⲛⲜⲝⲞⲟⲠⲡⲢⲣⲤⲥⲦⲧⲨⲩⲪⲫⲬⲭⲮⲯⲰⲱⲲⲳⲴⲵⲶⲷⲸⲹⲺⲻⲼⲽⲾⲿ +ⳀⳁⳂⳃⳄⳅⳆⳇⳈⳉⳊⳋⳌⳍⳎⳏⳐⳑⳒⳓⳔⳕⳖⳗⳘⳙⳚⳛⳜⳝⳞⳟⳠⳡⳢⳣⳤ⳥⳦⳧⳨⳩⳪ⳫⳬⳭⳮ⳯⳰⳱Ⳳⳳ⳴⳵⳶⳷⳸⳹⳺⳻⳼⳽⳾⳿ +ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥ⴦ⴧ⴨⴩⴪⴫⴬ⴭ⴮⴯ⴰⴱⴲⴳⴴⴵⴶⴷⴸⴹⴺⴻⴼⴽⴾⴿ +ⵀⵁⵂⵃⵄⵅⵆⵇⵈⵉⵊⵋⵌⵍⵎⵏⵐⵑⵒⵓⵔⵕⵖⵗⵘⵙⵚⵛⵜⵝⵞⵟⵠⵡⵢⵣⵤⵥⵦⵧ⵨⵩⵪⵫⵬⵭⵮ⵯ⵰⵱⵲⵳⵴⵵⵶⵷⵸⵹⵺⵻⵼⵽⵾⵿ +ⶀⶁⶂⶃⶄⶅⶆⶇⶈⶉⶊⶋⶌⶍⶎⶏⶐⶑⶒⶓⶔⶕⶖ⶗⶘⶙⶚⶛⶜⶝⶞⶟ⶠⶡⶢⶣⶤⶥⶦ⶧ⶨⶩⶪⶫⶬⶭⶮ⶯ⶰⶱⶲⶳⶴⶵⶶ⶷ⶸⶹⶺⶻⶼⶽⶾ⶿ +ⷀⷁⷂⷃⷄⷅⷆ⷇ⷈⷉⷊⷋⷌⷍⷎ⷏ⷐⷑⷒⷓⷔⷕⷖ⷗ⷘⷙⷚⷛⷜⷝⷞ⷟ⷠⷡⷢⷣⷤⷥⷦⷧⷨⷩⷪⷫⷬⷭⷮⷯⷰⷱⷲⷳⷴⷵⷶⷷⷸⷹⷺⷻⷼⷽⷾⷿ +⸀⸁⸂⸃⸄⸅⸆⸇⸈⸉⸊⸋⸌⸍⸎⸏⸐⸑⸒⸓⸔⸕⸖⸗⸘⸙⸚⸛⸜⸝⸞⸟⸠⸡⸢⸣⸤⸥⸦⸧⸨⸩⸪⸫⸬⸭⸮ⸯ⸰⸱⸲⸳⸴⸵⸶⸷⸸⸹⸺⸻⸼⸽⸾⸿ +⹀⹁⹂⹃⹄⹅⹆⹇⹈⹉⹊⹋⹌⹍⹎⹏⹐⹑⹒⹓⹔⹕⹖⹗⹘⹙⹚⹛⹜⹝⹞⹟⹠⹡⹢⹣⹤⹥⹦⹧⹨⹩⹪⹫⹬⹭⹮⹯⹰⹱⹲⹳⹴⹵⹶⹷⹸⹹⹺⹻⹼⹽⹾⹿ + +CJK Radicals Supplement (U+2E80-U+2EFF): + +⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺚⺛⺜⺝⺞⺟ +⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿ +⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟ +⻠⻡⻢⻣⻤⻥⻦⻧⻨⻩⻪⻫⻬⻭⻮⻯⻰⻱⻲⻳⻴⻵⻶⻷⻸⻹⻺⻻⻼⻽⻾⻿ + +Kangxi Radicals (U+2F00-U+2FDF): + +⼀⼁⼂⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌⼍⼎⼏⼐⼑⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜⼝⼞⼟ +⼠⼡⼢⼣⼤⼥⼦⼧⼨⼩⼪⼫⼬⼭⼮⼯⼰⼱⼲⼳⼴⼵⼶⼷⼸⼹⼺⼻⼼⼽⼾⼿ +⽀⽁⽂⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟ +⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿ +⾀⾁⾂⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌⾍⾎⾏⾐⾑⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜⾝⾞⾟ +⾠⾡⾢⾣⾤⾥⾦⾧⾨⾩⾪⾫⾬⾭⾮⾯⾰⾱⾲⾳⾴⾵⾶⾷⾸⾹⾺⾻⾼⾽⾾⾿ +⿀⿁⿂⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌⿍⿎⿏⿐⿑⿒⿓⿔⿕⿖⿗⿘⿙⿚⿛⿜⿝⿞⿟ + +Free block (U+2FE0-U+2FEF): + +⿠⿡⿢⿣⿤⿥⿦⿧⿨⿩⿪⿫⿬⿭⿮⿯ + +Ideographic Description Characters (U+2FF0-U+2FFF): + +⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻⿼⿽⿾⿿ + +CJK Symbols and Punctuation (U+3000-U+303F): + + 、。〃〄々〆〇〈〉《》「」『』【】〒〓〔〕〖〗〘〙〚〛〜〝〞〟 +〠〡〢〣〤〥〦〧〨〩◌〪◌〫◌〬◌〭◌〮◌〯〰〱〲〳〴〵〶〷〸〹〺〻〼〽〾〿 + +Hiragana (U+3040-U+309F): + +぀ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞた +だちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみ +むめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ゗゘◌゙◌゚゛゜ゝゞゟ + +Katakana (U+30A0-U+30FF): + +゠ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタ +ダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミ +ムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿ + +Bopomofo (U+3100-U+312F): + +㄀㄁㄂㄃㄄ㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟ +ㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩㄪㄫㄬㄭㄮㄯ + +Hangul Compatibility Jamo (U+3130-U+318F): + +㄰ㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎㅏ +ㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣㅤㅥㅦㅧㅨㅩㅪㅫㅬㅭㅮㅯ +ㅰㅱㅲㅳㅴㅵㅶㅷㅸㅹㅺㅻㅼㅽㅾㅿㆀㆁㆂㆃㆄㆅㆆㆇㆈㆉㆊㆋㆌㆍㆎ㆏ + +Kanbun (U+3190-U+319F): + +㆐㆑㆒㆓㆔㆕㆖㆗㆘㆙㆚㆛㆜㆝㆞㆟ + +Bopomofo Extended (U+31A0-U+31BF): + +ㆠㆡㆢㆣㆤㆥㆦㆧㆨㆩㆪㆫㆬㆭㆮㆯㆰㆱㆲㆳㆴㆵㆶㆷㆸㆹㆺㆻㆼㆽㆾㆿ + +Free block (U+31C0-U+31EF): + +㇀㇁㇂㇃㇄㇅㇆㇇㇈㇉㇊㇋㇌㇍㇎㇏㇐㇑㇒㇓㇔㇕㇖㇗㇘㇙㇚㇛㇜㇝㇞㇟ +㇠㇡㇢㇣㇤㇥㇦㇧㇨㇩㇪㇫㇬㇭㇮㇯ + +Katakana Phonetic Extensions (U+31F0-U+31FF): + +ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ + +Enclosed CJK Letters and Months (U+3200-U+32FF): + +㈀㈁㈂㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌㈍㈎㈏㈐㈑㈒㈓㈔㈕㈖㈗㈘㈙㈚㈛㈜㈝㈞㈟ +㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿ +㉀㉁㉂㉃㉄㉅㉆㉇㉈㉉㉊㉋㉌㉍㉎㉏㉐㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟ +㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉼㉽㉾㉿ +㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗㊘㊙㊚㊛㊜㊝㊞㊟ +㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿ +㋀㋁㋂㋃㋄㋅㋆㋇㋈㋉㋊㋋㋌㋍㋎㋏㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟ +㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋽㋾㋿ + +CJK Compatibility (U+3300-U+33FF): + +㌀㌁㌂㌃㌄㌅㌆㌇㌈㌉㌊㌋㌌㌍㌎㌏㌐㌑㌒㌓㌔㌕㌖㌗㌘㌙㌚㌛㌜㌝㌞㌟ +㌠㌡㌢㌣㌤㌥㌦㌧㌨㌩㌪㌫㌬㌭㌮㌯㌰㌱㌲㌳㌴㌵㌶㌷㌸㌹㌺㌻㌼㌽㌾㌿ +㍀㍁㍂㍃㍄㍅㍆㍇㍈㍉㍊㍋㍌㍍㍎㍏㍐㍑㍒㍓㍔㍕㍖㍗㍘㍙㍚㍛㍜㍝㍞㍟ +㍠㍡㍢㍣㍤㍥㍦㍧㍨㍩㍪㍫㍬㍭㍮㍯㍰㍱㍲㍳㍴㍵㍶㍷㍸㍹㍺㍻㍼㍽㍾㍿ +㎀㎁㎂㎃㎄㎅㎆㎇㎈㎉㎊㎋㎌㎍㎎㎏㎐㎑㎒㎓㎔㎕㎖㎗㎘㎙㎚㎛㎜㎝㎞㎟ +㎠㎡㎢㎣㎤㎥㎦㎧㎨㎩㎪㎫㎬㎭㎮㎯㎰㎱㎲㎳㎴㎵㎶㎷㎸㎹㎺㎻㎼㎽㎾㎿ +㏀㏁㏂㏃㏄㏅㏆㏇㏈㏉㏊㏋㏌㏍㏎㏏㏐㏑㏒㏓㏔㏕㏖㏗㏘㏙㏚㏛㏜㏝㏞㏟ +㏠㏡㏢㏣㏤㏥㏦㏧㏨㏩㏪㏫㏬㏭㏮㏯㏰㏱㏲㏳㏴㏵㏶㏷㏸㏹㏺㏻㏼㏽㏾㏿ + +CJK Unified Ideographs Extension A (U+3400-U+4DBF): + +㐀㐁㐂㐃㐄㐅㐆㐇㐈㐉㐊㐋㐌㐍㐎㐏㐐㐑㐒㐓㐔㐕㐖㐗㐘㐙㐚㐛㐜㐝㐞㐟 +㐠㐡㐢㐣㐤㐥㐦㐧㐨㐩㐪㐫㐬㐭㐮㐯㐰㐱㐲㐳㐴㐵㐶㐷㐸㐹㐺㐻㐼㐽㐾㐿 +㑀㑁㑂㑃㑄㑅㑆㑇㑈㑉㑊㑋㑌㑍㑎㑏㑐㑑㑒㑓㑔㑕㑖㑗㑘㑙㑚㑛㑜㑝㑞㑟 +㑠㑡㑢㑣㑤㑥㑦㑧㑨㑩㑪㑫㑬㑭㑮㑯㑰㑱㑲㑳㑴㑵㑶㑷㑸㑹㑺㑻㑼㑽㑾㑿 +㒀㒁㒂㒃㒄㒅㒆㒇㒈㒉㒊㒋㒌㒍㒎㒏㒐㒑㒒㒓㒔㒕㒖㒗㒘㒙㒚㒛㒜㒝㒞㒟 +㒠㒡㒢㒣㒤㒥㒦㒧㒨㒩㒪㒫㒬㒭㒮㒯㒰㒱㒲㒳㒴㒵㒶㒷㒸㒹㒺㒻㒼㒽㒾㒿 +㓀㓁㓂㓃㓄㓅㓆㓇㓈㓉㓊㓋㓌㓍㓎㓏㓐㓑㓒㓓㓔㓕㓖㓗㓘㓙㓚㓛㓜㓝㓞㓟 +㓠㓡㓢㓣㓤㓥㓦㓧㓨㓩㓪㓫㓬㓭㓮㓯㓰㓱㓲㓳㓴㓵㓶㓷㓸㓹㓺㓻㓼㓽㓾㓿 +㔀㔁㔂㔃㔄㔅㔆㔇㔈㔉㔊㔋㔌㔍㔎㔏㔐㔑㔒㔓㔔㔕㔖㔗㔘㔙㔚㔛㔜㔝㔞㔟 +㔠㔡㔢㔣㔤㔥㔦㔧㔨㔩㔪㔫㔬㔭㔮㔯㔰㔱㔲㔳㔴㔵㔶㔷㔸㔹㔺㔻㔼㔽㔾㔿 +㕀㕁㕂㕃㕄㕅㕆㕇㕈㕉㕊㕋㕌㕍㕎㕏㕐㕑㕒㕓㕔㕕㕖㕗㕘㕙㕚㕛㕜㕝㕞㕟 +㕠㕡㕢㕣㕤㕥㕦㕧㕨㕩㕪㕫㕬㕭㕮㕯㕰㕱㕲㕳㕴㕵㕶㕷㕸㕹㕺㕻㕼㕽㕾㕿 +㖀㖁㖂㖃㖄㖅㖆㖇㖈㖉㖊㖋㖌㖍㖎㖏㖐㖑㖒㖓㖔㖕㖖㖗㖘㖙㖚㖛㖜㖝㖞㖟 +㖠㖡㖢㖣㖤㖥㖦㖧㖨㖩㖪㖫㖬㖭㖮㖯㖰㖱㖲㖳㖴㖵㖶㖷㖸㖹㖺㖻㖼㖽㖾㖿 +㗀㗁㗂㗃㗄㗅㗆㗇㗈㗉㗊㗋㗌㗍㗎㗏㗐㗑㗒㗓㗔㗕㗖㗗㗘㗙㗚㗛㗜㗝㗞㗟 +㗠㗡㗢㗣㗤㗥㗦㗧㗨㗩㗪㗫㗬㗭㗮㗯㗰㗱㗲㗳㗴㗵㗶㗷㗸㗹㗺㗻㗼㗽㗾㗿 +㘀㘁㘂㘃㘄㘅㘆㘇㘈㘉㘊㘋㘌㘍㘎㘏㘐㘑㘒㘓㘔㘕㘖㘗㘘㘙㘚㘛㘜㘝㘞㘟 +㘠㘡㘢㘣㘤㘥㘦㘧㘨㘩㘪㘫㘬㘭㘮㘯㘰㘱㘲㘳㘴㘵㘶㘷㘸㘹㘺㘻㘼㘽㘾㘿 +㙀㙁㙂㙃㙄㙅㙆㙇㙈㙉㙊㙋㙌㙍㙎㙏㙐㙑㙒㙓㙔㙕㙖㙗㙘㙙㙚㙛㙜㙝㙞㙟 +㙠㙡㙢㙣㙤㙥㙦㙧㙨㙩㙪㙫㙬㙭㙮㙯㙰㙱㙲㙳㙴㙵㙶㙷㙸㙹㙺㙻㙼㙽㙾㙿 +㚀㚁㚂㚃㚄㚅㚆㚇㚈㚉㚊㚋㚌㚍㚎㚏㚐㚑㚒㚓㚔㚕㚖㚗㚘㚙㚚㚛㚜㚝㚞㚟 +㚠㚡㚢㚣㚤㚥㚦㚧㚨㚩㚪㚫㚬㚭㚮㚯㚰㚱㚲㚳㚴㚵㚶㚷㚸㚹㚺㚻㚼㚽㚾㚿 +㛀㛁㛂㛃㛄㛅㛆㛇㛈㛉㛊㛋㛌㛍㛎㛏㛐㛑㛒㛓㛔㛕㛖㛗㛘㛙㛚㛛㛜㛝㛞㛟 +㛠㛡㛢㛣㛤㛥㛦㛧㛨㛩㛪㛫㛬㛭㛮㛯㛰㛱㛲㛳㛴㛵㛶㛷㛸㛹㛺㛻㛼㛽㛾㛿 +㜀㜁㜂㜃㜄㜅㜆㜇㜈㜉㜊㜋㜌㜍㜎㜏㜐㜑㜒㜓㜔㜕㜖㜗㜘㜙㜚㜛㜜㜝㜞㜟 +㜠㜡㜢㜣㜤㜥㜦㜧㜨㜩㜪㜫㜬㜭㜮㜯㜰㜱㜲㜳㜴㜵㜶㜷㜸㜹㜺㜻㜼㜽㜾㜿 +㝀㝁㝂㝃㝄㝅㝆㝇㝈㝉㝊㝋㝌㝍㝎㝏㝐㝑㝒㝓㝔㝕㝖㝗㝘㝙㝚㝛㝜㝝㝞㝟 +㝠㝡㝢㝣㝤㝥㝦㝧㝨㝩㝪㝫㝬㝭㝮㝯㝰㝱㝲㝳㝴㝵㝶㝷㝸㝹㝺㝻㝼㝽㝾㝿 +㞀㞁㞂㞃㞄㞅㞆㞇㞈㞉㞊㞋㞌㞍㞎㞏㞐㞑㞒㞓㞔㞕㞖㞗㞘㞙㞚㞛㞜㞝㞞㞟 +㞠㞡㞢㞣㞤㞥㞦㞧㞨㞩㞪㞫㞬㞭㞮㞯㞰㞱㞲㞳㞴㞵㞶㞷㞸㞹㞺㞻㞼㞽㞾㞿 +㟀㟁㟂㟃㟄㟅㟆㟇㟈㟉㟊㟋㟌㟍㟎㟏㟐㟑㟒㟓㟔㟕㟖㟗㟘㟙㟚㟛㟜㟝㟞㟟 +㟠㟡㟢㟣㟤㟥㟦㟧㟨㟩㟪㟫㟬㟭㟮㟯㟰㟱㟲㟳㟴㟵㟶㟷㟸㟹㟺㟻㟼㟽㟾㟿 +㠀㠁㠂㠃㠄㠅㠆㠇㠈㠉㠊㠋㠌㠍㠎㠏㠐㠑㠒㠓㠔㠕㠖㠗㠘㠙㠚㠛㠜㠝㠞㠟 +㠠㠡㠢㠣㠤㠥㠦㠧㠨㠩㠪㠫㠬㠭㠮㠯㠰㠱㠲㠳㠴㠵㠶㠷㠸㠹㠺㠻㠼㠽㠾㠿 +㡀㡁㡂㡃㡄㡅㡆㡇㡈㡉㡊㡋㡌㡍㡎㡏㡐㡑㡒㡓㡔㡕㡖㡗㡘㡙㡚㡛㡜㡝㡞㡟 +㡠㡡㡢㡣㡤㡥㡦㡧㡨㡩㡪㡫㡬㡭㡮㡯㡰㡱㡲㡳㡴㡵㡶㡷㡸㡹㡺㡻㡼㡽㡾㡿 +㢀㢁㢂㢃㢄㢅㢆㢇㢈㢉㢊㢋㢌㢍㢎㢏㢐㢑㢒㢓㢔㢕㢖㢗㢘㢙㢚㢛㢜㢝㢞㢟 +㢠㢡㢢㢣㢤㢥㢦㢧㢨㢩㢪㢫㢬㢭㢮㢯㢰㢱㢲㢳㢴㢵㢶㢷㢸㢹㢺㢻㢼㢽㢾㢿 +㣀㣁㣂㣃㣄㣅㣆㣇㣈㣉㣊㣋㣌㣍㣎㣏㣐㣑㣒㣓㣔㣕㣖㣗㣘㣙㣚㣛㣜㣝㣞㣟 +㣠㣡㣢㣣㣤㣥㣦㣧㣨㣩㣪㣫㣬㣭㣮㣯㣰㣱㣲㣳㣴㣵㣶㣷㣸㣹㣺㣻㣼㣽㣾㣿 +㤀㤁㤂㤃㤄㤅㤆㤇㤈㤉㤊㤋㤌㤍㤎㤏㤐㤑㤒㤓㤔㤕㤖㤗㤘㤙㤚㤛㤜㤝㤞㤟 +㤠㤡㤢㤣㤤㤥㤦㤧㤨㤩㤪㤫㤬㤭㤮㤯㤰㤱㤲㤳㤴㤵㤶㤷㤸㤹㤺㤻㤼㤽㤾㤿 +㥀㥁㥂㥃㥄㥅㥆㥇㥈㥉㥊㥋㥌㥍㥎㥏㥐㥑㥒㥓㥔㥕㥖㥗㥘㥙㥚㥛㥜㥝㥞㥟 +㥠㥡㥢㥣㥤㥥㥦㥧㥨㥩㥪㥫㥬㥭㥮㥯㥰㥱㥲㥳㥴㥵㥶㥷㥸㥹㥺㥻㥼㥽㥾㥿 +㦀㦁㦂㦃㦄㦅㦆㦇㦈㦉㦊㦋㦌㦍㦎㦏㦐㦑㦒㦓㦔㦕㦖㦗㦘㦙㦚㦛㦜㦝㦞㦟 +㦠㦡㦢㦣㦤㦥㦦㦧㦨㦩㦪㦫㦬㦭㦮㦯㦰㦱㦲㦳㦴㦵㦶㦷㦸㦹㦺㦻㦼㦽㦾㦿 +㧀㧁㧂㧃㧄㧅㧆㧇㧈㧉㧊㧋㧌㧍㧎㧏㧐㧑㧒㧓㧔㧕㧖㧗㧘㧙㧚㧛㧜㧝㧞㧟 +㧠㧡㧢㧣㧤㧥㧦㧧㧨㧩㧪㧫㧬㧭㧮㧯㧰㧱㧲㧳㧴㧵㧶㧷㧸㧹㧺㧻㧼㧽㧾㧿 +㨀㨁㨂㨃㨄㨅㨆㨇㨈㨉㨊㨋㨌㨍㨎㨏㨐㨑㨒㨓㨔㨕㨖㨗㨘㨙㨚㨛㨜㨝㨞㨟 +㨠㨡㨢㨣㨤㨥㨦㨧㨨㨩㨪㨫㨬㨭㨮㨯㨰㨱㨲㨳㨴㨵㨶㨷㨸㨹㨺㨻㨼㨽㨾㨿 +㩀㩁㩂㩃㩄㩅㩆㩇㩈㩉㩊㩋㩌㩍㩎㩏㩐㩑㩒㩓㩔㩕㩖㩗㩘㩙㩚㩛㩜㩝㩞㩟 +㩠㩡㩢㩣㩤㩥㩦㩧㩨㩩㩪㩫㩬㩭㩮㩯㩰㩱㩲㩳㩴㩵㩶㩷㩸㩹㩺㩻㩼㩽㩾㩿 +㪀㪁㪂㪃㪄㪅㪆㪇㪈㪉㪊㪋㪌㪍㪎㪏㪐㪑㪒㪓㪔㪕㪖㪗㪘㪙㪚㪛㪜㪝㪞㪟 +㪠㪡㪢㪣㪤㪥㪦㪧㪨㪩㪪㪫㪬㪭㪮㪯㪰㪱㪲㪳㪴㪵㪶㪷㪸㪹㪺㪻㪼㪽㪾㪿 +㫀㫁㫂㫃㫄㫅㫆㫇㫈㫉㫊㫋㫌㫍㫎㫏㫐㫑㫒㫓㫔㫕㫖㫗㫘㫙㫚㫛㫜㫝㫞㫟 +㫠㫡㫢㫣㫤㫥㫦㫧㫨㫩㫪㫫㫬㫭㫮㫯㫰㫱㫲㫳㫴㫵㫶㫷㫸㫹㫺㫻㫼㫽㫾㫿 +㬀㬁㬂㬃㬄㬅㬆㬇㬈㬉㬊㬋㬌㬍㬎㬏㬐㬑㬒㬓㬔㬕㬖㬗㬘㬙㬚㬛㬜㬝㬞㬟 +㬠㬡㬢㬣㬤㬥㬦㬧㬨㬩㬪㬫㬬㬭㬮㬯㬰㬱㬲㬳㬴㬵㬶㬷㬸㬹㬺㬻㬼㬽㬾㬿 +㭀㭁㭂㭃㭄㭅㭆㭇㭈㭉㭊㭋㭌㭍㭎㭏㭐㭑㭒㭓㭔㭕㭖㭗㭘㭙㭚㭛㭜㭝㭞㭟 +㭠㭡㭢㭣㭤㭥㭦㭧㭨㭩㭪㭫㭬㭭㭮㭯㭰㭱㭲㭳㭴㭵㭶㭷㭸㭹㭺㭻㭼㭽㭾㭿 +㮀㮁㮂㮃㮄㮅㮆㮇㮈㮉㮊㮋㮌㮍㮎㮏㮐㮑㮒㮓㮔㮕㮖㮗㮘㮙㮚㮛㮜㮝㮞㮟 +㮠㮡㮢㮣㮤㮥㮦㮧㮨㮩㮪㮫㮬㮭㮮㮯㮰㮱㮲㮳㮴㮵㮶㮷㮸㮹㮺㮻㮼㮽㮾㮿 +㯀㯁㯂㯃㯄㯅㯆㯇㯈㯉㯊㯋㯌㯍㯎㯏㯐㯑㯒㯓㯔㯕㯖㯗㯘㯙㯚㯛㯜㯝㯞㯟 +㯠㯡㯢㯣㯤㯥㯦㯧㯨㯩㯪㯫㯬㯭㯮㯯㯰㯱㯲㯳㯴㯵㯶㯷㯸㯹㯺㯻㯼㯽㯾㯿 +㰀㰁㰂㰃㰄㰅㰆㰇㰈㰉㰊㰋㰌㰍㰎㰏㰐㰑㰒㰓㰔㰕㰖㰗㰘㰙㰚㰛㰜㰝㰞㰟 +㰠㰡㰢㰣㰤㰥㰦㰧㰨㰩㰪㰫㰬㰭㰮㰯㰰㰱㰲㰳㰴㰵㰶㰷㰸㰹㰺㰻㰼㰽㰾㰿 +㱀㱁㱂㱃㱄㱅㱆㱇㱈㱉㱊㱋㱌㱍㱎㱏㱐㱑㱒㱓㱔㱕㱖㱗㱘㱙㱚㱛㱜㱝㱞㱟 +㱠㱡㱢㱣㱤㱥㱦㱧㱨㱩㱪㱫㱬㱭㱮㱯㱰㱱㱲㱳㱴㱵㱶㱷㱸㱹㱺㱻㱼㱽㱾㱿 +㲀㲁㲂㲃㲄㲅㲆㲇㲈㲉㲊㲋㲌㲍㲎㲏㲐㲑㲒㲓㲔㲕㲖㲗㲘㲙㲚㲛㲜㲝㲞㲟 +㲠㲡㲢㲣㲤㲥㲦㲧㲨㲩㲪㲫㲬㲭㲮㲯㲰㲱㲲㲳㲴㲵㲶㲷㲸㲹㲺㲻㲼㲽㲾㲿 +㳀㳁㳂㳃㳄㳅㳆㳇㳈㳉㳊㳋㳌㳍㳎㳏㳐㳑㳒㳓㳔㳕㳖㳗㳘㳙㳚㳛㳜㳝㳞㳟 +㳠㳡㳢㳣㳤㳥㳦㳧㳨㳩㳪㳫㳬㳭㳮㳯㳰㳱㳲㳳㳴㳵㳶㳷㳸㳹㳺㳻㳼㳽㳾㳿 +㴀㴁㴂㴃㴄㴅㴆㴇㴈㴉㴊㴋㴌㴍㴎㴏㴐㴑㴒㴓㴔㴕㴖㴗㴘㴙㴚㴛㴜㴝㴞㴟 +㴠㴡㴢㴣㴤㴥㴦㴧㴨㴩㴪㴫㴬㴭㴮㴯㴰㴱㴲㴳㴴㴵㴶㴷㴸㴹㴺㴻㴼㴽㴾㴿 +㵀㵁㵂㵃㵄㵅㵆㵇㵈㵉㵊㵋㵌㵍㵎㵏㵐㵑㵒㵓㵔㵕㵖㵗㵘㵙㵚㵛㵜㵝㵞㵟 +㵠㵡㵢㵣㵤㵥㵦㵧㵨㵩㵪㵫㵬㵭㵮㵯㵰㵱㵲㵳㵴㵵㵶㵷㵸㵹㵺㵻㵼㵽㵾㵿 +㶀㶁㶂㶃㶄㶅㶆㶇㶈㶉㶊㶋㶌㶍㶎㶏㶐㶑㶒㶓㶔㶕㶖㶗㶘㶙㶚㶛㶜㶝㶞㶟 +㶠㶡㶢㶣㶤㶥㶦㶧㶨㶩㶪㶫㶬㶭㶮㶯㶰㶱㶲㶳㶴㶵㶶㶷㶸㶹㶺㶻㶼㶽㶾㶿 +㷀㷁㷂㷃㷄㷅㷆㷇㷈㷉㷊㷋㷌㷍㷎㷏㷐㷑㷒㷓㷔㷕㷖㷗㷘㷙㷚㷛㷜㷝㷞㷟 +㷠㷡㷢㷣㷤㷥㷦㷧㷨㷩㷪㷫㷬㷭㷮㷯㷰㷱㷲㷳㷴㷵㷶㷷㷸㷹㷺㷻㷼㷽㷾㷿 +㸀㸁㸂㸃㸄㸅㸆㸇㸈㸉㸊㸋㸌㸍㸎㸏㸐㸑㸒㸓㸔㸕㸖㸗㸘㸙㸚㸛㸜㸝㸞㸟 +㸠㸡㸢㸣㸤㸥㸦㸧㸨㸩㸪㸫㸬㸭㸮㸯㸰㸱㸲㸳㸴㸵㸶㸷㸸㸹㸺㸻㸼㸽㸾㸿 +㹀㹁㹂㹃㹄㹅㹆㹇㹈㹉㹊㹋㹌㹍㹎㹏㹐㹑㹒㹓㹔㹕㹖㹗㹘㹙㹚㹛㹜㹝㹞㹟 +㹠㹡㹢㹣㹤㹥㹦㹧㹨㹩㹪㹫㹬㹭㹮㹯㹰㹱㹲㹳㹴㹵㹶㹷㹸㹹㹺㹻㹼㹽㹾㹿 +㺀㺁㺂㺃㺄㺅㺆㺇㺈㺉㺊㺋㺌㺍㺎㺏㺐㺑㺒㺓㺔㺕㺖㺗㺘㺙㺚㺛㺜㺝㺞㺟 +㺠㺡㺢㺣㺤㺥㺦㺧㺨㺩㺪㺫㺬㺭㺮㺯㺰㺱㺲㺳㺴㺵㺶㺷㺸㺹㺺㺻㺼㺽㺾㺿 +㻀㻁㻂㻃㻄㻅㻆㻇㻈㻉㻊㻋㻌㻍㻎㻏㻐㻑㻒㻓㻔㻕㻖㻗㻘㻙㻚㻛㻜㻝㻞㻟 +㻠㻡㻢㻣㻤㻥㻦㻧㻨㻩㻪㻫㻬㻭㻮㻯㻰㻱㻲㻳㻴㻵㻶㻷㻸㻹㻺㻻㻼㻽㻾㻿 +㼀㼁㼂㼃㼄㼅㼆㼇㼈㼉㼊㼋㼌㼍㼎㼏㼐㼑㼒㼓㼔㼕㼖㼗㼘㼙㼚㼛㼜㼝㼞㼟 +㼠㼡㼢㼣㼤㼥㼦㼧㼨㼩㼪㼫㼬㼭㼮㼯㼰㼱㼲㼳㼴㼵㼶㼷㼸㼹㼺㼻㼼㼽㼾㼿 +㽀㽁㽂㽃㽄㽅㽆㽇㽈㽉㽊㽋㽌㽍㽎㽏㽐㽑㽒㽓㽔㽕㽖㽗㽘㽙㽚㽛㽜㽝㽞㽟 +㽠㽡㽢㽣㽤㽥㽦㽧㽨㽩㽪㽫㽬㽭㽮㽯㽰㽱㽲㽳㽴㽵㽶㽷㽸㽹㽺㽻㽼㽽㽾㽿 +㾀㾁㾂㾃㾄㾅㾆㾇㾈㾉㾊㾋㾌㾍㾎㾏㾐㾑㾒㾓㾔㾕㾖㾗㾘㾙㾚㾛㾜㾝㾞㾟 +㾠㾡㾢㾣㾤㾥㾦㾧㾨㾩㾪㾫㾬㾭㾮㾯㾰㾱㾲㾳㾴㾵㾶㾷㾸㾹㾺㾻㾼㾽㾾㾿 +㿀㿁㿂㿃㿄㿅㿆㿇㿈㿉㿊㿋㿌㿍㿎㿏㿐㿑㿒㿓㿔㿕㿖㿗㿘㿙㿚㿛㿜㿝㿞㿟 +㿠㿡㿢㿣㿤㿥㿦㿧㿨㿩㿪㿫㿬㿭㿮㿯㿰㿱㿲㿳㿴㿵㿶㿷㿸㿹㿺㿻㿼㿽㿾㿿 +䀀䀁䀂䀃䀄䀅䀆䀇䀈䀉䀊䀋䀌䀍䀎䀏䀐䀑䀒䀓䀔䀕䀖䀗䀘䀙䀚䀛䀜䀝䀞䀟 +䀠䀡䀢䀣䀤䀥䀦䀧䀨䀩䀪䀫䀬䀭䀮䀯䀰䀱䀲䀳䀴䀵䀶䀷䀸䀹䀺䀻䀼䀽䀾䀿 +䁀䁁䁂䁃䁄䁅䁆䁇䁈䁉䁊䁋䁌䁍䁎䁏䁐䁑䁒䁓䁔䁕䁖䁗䁘䁙䁚䁛䁜䁝䁞䁟 +䁠䁡䁢䁣䁤䁥䁦䁧䁨䁩䁪䁫䁬䁭䁮䁯䁰䁱䁲䁳䁴䁵䁶䁷䁸䁹䁺䁻䁼䁽䁾䁿 +䂀䂁䂂䂃䂄䂅䂆䂇䂈䂉䂊䂋䂌䂍䂎䂏䂐䂑䂒䂓䂔䂕䂖䂗䂘䂙䂚䂛䂜䂝䂞䂟 +䂠䂡䂢䂣䂤䂥䂦䂧䂨䂩䂪䂫䂬䂭䂮䂯䂰䂱䂲䂳䂴䂵䂶䂷䂸䂹䂺䂻䂼䂽䂾䂿 +䃀䃁䃂䃃䃄䃅䃆䃇䃈䃉䃊䃋䃌䃍䃎䃏䃐䃑䃒䃓䃔䃕䃖䃗䃘䃙䃚䃛䃜䃝䃞䃟 +䃠䃡䃢䃣䃤䃥䃦䃧䃨䃩䃪䃫䃬䃭䃮䃯䃰䃱䃲䃳䃴䃵䃶䃷䃸䃹䃺䃻䃼䃽䃾䃿 +䄀䄁䄂䄃䄄䄅䄆䄇䄈䄉䄊䄋䄌䄍䄎䄏䄐䄑䄒䄓䄔䄕䄖䄗䄘䄙䄚䄛䄜䄝䄞䄟 +䄠䄡䄢䄣䄤䄥䄦䄧䄨䄩䄪䄫䄬䄭䄮䄯䄰䄱䄲䄳䄴䄵䄶䄷䄸䄹䄺䄻䄼䄽䄾䄿 +䅀䅁䅂䅃䅄䅅䅆䅇䅈䅉䅊䅋䅌䅍䅎䅏䅐䅑䅒䅓䅔䅕䅖䅗䅘䅙䅚䅛䅜䅝䅞䅟 +䅠䅡䅢䅣䅤䅥䅦䅧䅨䅩䅪䅫䅬䅭䅮䅯䅰䅱䅲䅳䅴䅵䅶䅷䅸䅹䅺䅻䅼䅽䅾䅿 +䆀䆁䆂䆃䆄䆅䆆䆇䆈䆉䆊䆋䆌䆍䆎䆏䆐䆑䆒䆓䆔䆕䆖䆗䆘䆙䆚䆛䆜䆝䆞䆟 +䆠䆡䆢䆣䆤䆥䆦䆧䆨䆩䆪䆫䆬䆭䆮䆯䆰䆱䆲䆳䆴䆵䆶䆷䆸䆹䆺䆻䆼䆽䆾䆿 +䇀䇁䇂䇃䇄䇅䇆䇇䇈䇉䇊䇋䇌䇍䇎䇏䇐䇑䇒䇓䇔䇕䇖䇗䇘䇙䇚䇛䇜䇝䇞䇟 +䇠䇡䇢䇣䇤䇥䇦䇧䇨䇩䇪䇫䇬䇭䇮䇯䇰䇱䇲䇳䇴䇵䇶䇷䇸䇹䇺䇻䇼䇽䇾䇿 +䈀䈁䈂䈃䈄䈅䈆䈇䈈䈉䈊䈋䈌䈍䈎䈏䈐䈑䈒䈓䈔䈕䈖䈗䈘䈙䈚䈛䈜䈝䈞䈟 +䈠䈡䈢䈣䈤䈥䈦䈧䈨䈩䈪䈫䈬䈭䈮䈯䈰䈱䈲䈳䈴䈵䈶䈷䈸䈹䈺䈻䈼䈽䈾䈿 +䉀䉁䉂䉃䉄䉅䉆䉇䉈䉉䉊䉋䉌䉍䉎䉏䉐䉑䉒䉓䉔䉕䉖䉗䉘䉙䉚䉛䉜䉝䉞䉟 +䉠䉡䉢䉣䉤䉥䉦䉧䉨䉩䉪䉫䉬䉭䉮䉯䉰䉱䉲䉳䉴䉵䉶䉷䉸䉹䉺䉻䉼䉽䉾䉿 +䊀䊁䊂䊃䊄䊅䊆䊇䊈䊉䊊䊋䊌䊍䊎䊏䊐䊑䊒䊓䊔䊕䊖䊗䊘䊙䊚䊛䊜䊝䊞䊟 +䊠䊡䊢䊣䊤䊥䊦䊧䊨䊩䊪䊫䊬䊭䊮䊯䊰䊱䊲䊳䊴䊵䊶䊷䊸䊹䊺䊻䊼䊽䊾䊿 +䋀䋁䋂䋃䋄䋅䋆䋇䋈䋉䋊䋋䋌䋍䋎䋏䋐䋑䋒䋓䋔䋕䋖䋗䋘䋙䋚䋛䋜䋝䋞䋟 +䋠䋡䋢䋣䋤䋥䋦䋧䋨䋩䋪䋫䋬䋭䋮䋯䋰䋱䋲䋳䋴䋵䋶䋷䋸䋹䋺䋻䋼䋽䋾䋿 +䌀䌁䌂䌃䌄䌅䌆䌇䌈䌉䌊䌋䌌䌍䌎䌏䌐䌑䌒䌓䌔䌕䌖䌗䌘䌙䌚䌛䌜䌝䌞䌟 +䌠䌡䌢䌣䌤䌥䌦䌧䌨䌩䌪䌫䌬䌭䌮䌯䌰䌱䌲䌳䌴䌵䌶䌷䌸䌹䌺䌻䌼䌽䌾䌿 +䍀䍁䍂䍃䍄䍅䍆䍇䍈䍉䍊䍋䍌䍍䍎䍏䍐䍑䍒䍓䍔䍕䍖䍗䍘䍙䍚䍛䍜䍝䍞䍟 +䍠䍡䍢䍣䍤䍥䍦䍧䍨䍩䍪䍫䍬䍭䍮䍯䍰䍱䍲䍳䍴䍵䍶䍷䍸䍹䍺䍻䍼䍽䍾䍿 +䎀䎁䎂䎃䎄䎅䎆䎇䎈䎉䎊䎋䎌䎍䎎䎏䎐䎑䎒䎓䎔䎕䎖䎗䎘䎙䎚䎛䎜䎝䎞䎟 +䎠䎡䎢䎣䎤䎥䎦䎧䎨䎩䎪䎫䎬䎭䎮䎯䎰䎱䎲䎳䎴䎵䎶䎷䎸䎹䎺䎻䎼䎽䎾䎿 +䏀䏁䏂䏃䏄䏅䏆䏇䏈䏉䏊䏋䏌䏍䏎䏏䏐䏑䏒䏓䏔䏕䏖䏗䏘䏙䏚䏛䏜䏝䏞䏟 +䏠䏡䏢䏣䏤䏥䏦䏧䏨䏩䏪䏫䏬䏭䏮䏯䏰䏱䏲䏳䏴䏵䏶䏷䏸䏹䏺䏻䏼䏽䏾䏿 +䐀䐁䐂䐃䐄䐅䐆䐇䐈䐉䐊䐋䐌䐍䐎䐏䐐䐑䐒䐓䐔䐕䐖䐗䐘䐙䐚䐛䐜䐝䐞䐟 +䐠䐡䐢䐣䐤䐥䐦䐧䐨䐩䐪䐫䐬䐭䐮䐯䐰䐱䐲䐳䐴䐵䐶䐷䐸䐹䐺䐻䐼䐽䐾䐿 +䑀䑁䑂䑃䑄䑅䑆䑇䑈䑉䑊䑋䑌䑍䑎䑏䑐䑑䑒䑓䑔䑕䑖䑗䑘䑙䑚䑛䑜䑝䑞䑟 +䑠䑡䑢䑣䑤䑥䑦䑧䑨䑩䑪䑫䑬䑭䑮䑯䑰䑱䑲䑳䑴䑵䑶䑷䑸䑹䑺䑻䑼䑽䑾䑿 +䒀䒁䒂䒃䒄䒅䒆䒇䒈䒉䒊䒋䒌䒍䒎䒏䒐䒑䒒䒓䒔䒕䒖䒗䒘䒙䒚䒛䒜䒝䒞䒟 +䒠䒡䒢䒣䒤䒥䒦䒧䒨䒩䒪䒫䒬䒭䒮䒯䒰䒱䒲䒳䒴䒵䒶䒷䒸䒹䒺䒻䒼䒽䒾䒿 +䓀䓁䓂䓃䓄䓅䓆䓇䓈䓉䓊䓋䓌䓍䓎䓏䓐䓑䓒䓓䓔䓕䓖䓗䓘䓙䓚䓛䓜䓝䓞䓟 +䓠䓡䓢䓣䓤䓥䓦䓧䓨䓩䓪䓫䓬䓭䓮䓯䓰䓱䓲䓳䓴䓵䓶䓷䓸䓹䓺䓻䓼䓽䓾䓿 +䔀䔁䔂䔃䔄䔅䔆䔇䔈䔉䔊䔋䔌䔍䔎䔏䔐䔑䔒䔓䔔䔕䔖䔗䔘䔙䔚䔛䔜䔝䔞䔟 +䔠䔡䔢䔣䔤䔥䔦䔧䔨䔩䔪䔫䔬䔭䔮䔯䔰䔱䔲䔳䔴䔵䔶䔷䔸䔹䔺䔻䔼䔽䔾䔿 +䕀䕁䕂䕃䕄䕅䕆䕇䕈䕉䕊䕋䕌䕍䕎䕏䕐䕑䕒䕓䕔䕕䕖䕗䕘䕙䕚䕛䕜䕝䕞䕟 +䕠䕡䕢䕣䕤䕥䕦䕧䕨䕩䕪䕫䕬䕭䕮䕯䕰䕱䕲䕳䕴䕵䕶䕷䕸䕹䕺䕻䕼䕽䕾䕿 +䖀䖁䖂䖃䖄䖅䖆䖇䖈䖉䖊䖋䖌䖍䖎䖏䖐䖑䖒䖓䖔䖕䖖䖗䖘䖙䖚䖛䖜䖝䖞䖟 +䖠䖡䖢䖣䖤䖥䖦䖧䖨䖩䖪䖫䖬䖭䖮䖯䖰䖱䖲䖳䖴䖵䖶䖷䖸䖹䖺䖻䖼䖽䖾䖿 +䗀䗁䗂䗃䗄䗅䗆䗇䗈䗉䗊䗋䗌䗍䗎䗏䗐䗑䗒䗓䗔䗕䗖䗗䗘䗙䗚䗛䗜䗝䗞䗟 +䗠䗡䗢䗣䗤䗥䗦䗧䗨䗩䗪䗫䗬䗭䗮䗯䗰䗱䗲䗳䗴䗵䗶䗷䗸䗹䗺䗻䗼䗽䗾䗿 +䘀䘁䘂䘃䘄䘅䘆䘇䘈䘉䘊䘋䘌䘍䘎䘏䘐䘑䘒䘓䘔䘕䘖䘗䘘䘙䘚䘛䘜䘝䘞䘟 +䘠䘡䘢䘣䘤䘥䘦䘧䘨䘩䘪䘫䘬䘭䘮䘯䘰䘱䘲䘳䘴䘵䘶䘷䘸䘹䘺䘻䘼䘽䘾䘿 +䙀䙁䙂䙃䙄䙅䙆䙇䙈䙉䙊䙋䙌䙍䙎䙏䙐䙑䙒䙓䙔䙕䙖䙗䙘䙙䙚䙛䙜䙝䙞䙟 +䙠䙡䙢䙣䙤䙥䙦䙧䙨䙩䙪䙫䙬䙭䙮䙯䙰䙱䙲䙳䙴䙵䙶䙷䙸䙹䙺䙻䙼䙽䙾䙿 +䚀䚁䚂䚃䚄䚅䚆䚇䚈䚉䚊䚋䚌䚍䚎䚏䚐䚑䚒䚓䚔䚕䚖䚗䚘䚙䚚䚛䚜䚝䚞䚟 +䚠䚡䚢䚣䚤䚥䚦䚧䚨䚩䚪䚫䚬䚭䚮䚯䚰䚱䚲䚳䚴䚵䚶䚷䚸䚹䚺䚻䚼䚽䚾䚿 +䛀䛁䛂䛃䛄䛅䛆䛇䛈䛉䛊䛋䛌䛍䛎䛏䛐䛑䛒䛓䛔䛕䛖䛗䛘䛙䛚䛛䛜䛝䛞䛟 +䛠䛡䛢䛣䛤䛥䛦䛧䛨䛩䛪䛫䛬䛭䛮䛯䛰䛱䛲䛳䛴䛵䛶䛷䛸䛹䛺䛻䛼䛽䛾䛿 +䜀䜁䜂䜃䜄䜅䜆䜇䜈䜉䜊䜋䜌䜍䜎䜏䜐䜑䜒䜓䜔䜕䜖䜗䜘䜙䜚䜛䜜䜝䜞䜟 +䜠䜡䜢䜣䜤䜥䜦䜧䜨䜩䜪䜫䜬䜭䜮䜯䜰䜱䜲䜳䜴䜵䜶䜷䜸䜹䜺䜻䜼䜽䜾䜿 +䝀䝁䝂䝃䝄䝅䝆䝇䝈䝉䝊䝋䝌䝍䝎䝏䝐䝑䝒䝓䝔䝕䝖䝗䝘䝙䝚䝛䝜䝝䝞䝟 +䝠䝡䝢䝣䝤䝥䝦䝧䝨䝩䝪䝫䝬䝭䝮䝯䝰䝱䝲䝳䝴䝵䝶䝷䝸䝹䝺䝻䝼䝽䝾䝿 +䞀䞁䞂䞃䞄䞅䞆䞇䞈䞉䞊䞋䞌䞍䞎䞏䞐䞑䞒䞓䞔䞕䞖䞗䞘䞙䞚䞛䞜䞝䞞䞟 +䞠䞡䞢䞣䞤䞥䞦䞧䞨䞩䞪䞫䞬䞭䞮䞯䞰䞱䞲䞳䞴䞵䞶䞷䞸䞹䞺䞻䞼䞽䞾䞿 +䟀䟁䟂䟃䟄䟅䟆䟇䟈䟉䟊䟋䟌䟍䟎䟏䟐䟑䟒䟓䟔䟕䟖䟗䟘䟙䟚䟛䟜䟝䟞䟟 +䟠䟡䟢䟣䟤䟥䟦䟧䟨䟩䟪䟫䟬䟭䟮䟯䟰䟱䟲䟳䟴䟵䟶䟷䟸䟹䟺䟻䟼䟽䟾䟿 +䠀䠁䠂䠃䠄䠅䠆䠇䠈䠉䠊䠋䠌䠍䠎䠏䠐䠑䠒䠓䠔䠕䠖䠗䠘䠙䠚䠛䠜䠝䠞䠟 +䠠䠡䠢䠣䠤䠥䠦䠧䠨䠩䠪䠫䠬䠭䠮䠯䠰䠱䠲䠳䠴䠵䠶䠷䠸䠹䠺䠻䠼䠽䠾䠿 +䡀䡁䡂䡃䡄䡅䡆䡇䡈䡉䡊䡋䡌䡍䡎䡏䡐䡑䡒䡓䡔䡕䡖䡗䡘䡙䡚䡛䡜䡝䡞䡟 +䡠䡡䡢䡣䡤䡥䡦䡧䡨䡩䡪䡫䡬䡭䡮䡯䡰䡱䡲䡳䡴䡵䡶䡷䡸䡹䡺䡻䡼䡽䡾䡿 +䢀䢁䢂䢃䢄䢅䢆䢇䢈䢉䢊䢋䢌䢍䢎䢏䢐䢑䢒䢓䢔䢕䢖䢗䢘䢙䢚䢛䢜䢝䢞䢟 +䢠䢡䢢䢣䢤䢥䢦䢧䢨䢩䢪䢫䢬䢭䢮䢯䢰䢱䢲䢳䢴䢵䢶䢷䢸䢹䢺䢻䢼䢽䢾䢿 +䣀䣁䣂䣃䣄䣅䣆䣇䣈䣉䣊䣋䣌䣍䣎䣏䣐䣑䣒䣓䣔䣕䣖䣗䣘䣙䣚䣛䣜䣝䣞䣟 +䣠䣡䣢䣣䣤䣥䣦䣧䣨䣩䣪䣫䣬䣭䣮䣯䣰䣱䣲䣳䣴䣵䣶䣷䣸䣹䣺䣻䣼䣽䣾䣿 +䤀䤁䤂䤃䤄䤅䤆䤇䤈䤉䤊䤋䤌䤍䤎䤏䤐䤑䤒䤓䤔䤕䤖䤗䤘䤙䤚䤛䤜䤝䤞䤟 +䤠䤡䤢䤣䤤䤥䤦䤧䤨䤩䤪䤫䤬䤭䤮䤯䤰䤱䤲䤳䤴䤵䤶䤷䤸䤹䤺䤻䤼䤽䤾䤿 +䥀䥁䥂䥃䥄䥅䥆䥇䥈䥉䥊䥋䥌䥍䥎䥏䥐䥑䥒䥓䥔䥕䥖䥗䥘䥙䥚䥛䥜䥝䥞䥟 +䥠䥡䥢䥣䥤䥥䥦䥧䥨䥩䥪䥫䥬䥭䥮䥯䥰䥱䥲䥳䥴䥵䥶䥷䥸䥹䥺䥻䥼䥽䥾䥿 +䦀䦁䦂䦃䦄䦅䦆䦇䦈䦉䦊䦋䦌䦍䦎䦏䦐䦑䦒䦓䦔䦕䦖䦗䦘䦙䦚䦛䦜䦝䦞䦟 +䦠䦡䦢䦣䦤䦥䦦䦧䦨䦩䦪䦫䦬䦭䦮䦯䦰䦱䦲䦳䦴䦵䦶䦷䦸䦹䦺䦻䦼䦽䦾䦿 +䧀䧁䧂䧃䧄䧅䧆䧇䧈䧉䧊䧋䧌䧍䧎䧏䧐䧑䧒䧓䧔䧕䧖䧗䧘䧙䧚䧛䧜䧝䧞䧟 +䧠䧡䧢䧣䧤䧥䧦䧧䧨䧩䧪䧫䧬䧭䧮䧯䧰䧱䧲䧳䧴䧵䧶䧷䧸䧹䧺䧻䧼䧽䧾䧿 +䨀䨁䨂䨃䨄䨅䨆䨇䨈䨉䨊䨋䨌䨍䨎䨏䨐䨑䨒䨓䨔䨕䨖䨗䨘䨙䨚䨛䨜䨝䨞䨟 +䨠䨡䨢䨣䨤䨥䨦䨧䨨䨩䨪䨫䨬䨭䨮䨯䨰䨱䨲䨳䨴䨵䨶䨷䨸䨹䨺䨻䨼䨽䨾䨿 +䩀䩁䩂䩃䩄䩅䩆䩇䩈䩉䩊䩋䩌䩍䩎䩏䩐䩑䩒䩓䩔䩕䩖䩗䩘䩙䩚䩛䩜䩝䩞䩟 +䩠䩡䩢䩣䩤䩥䩦䩧䩨䩩䩪䩫䩬䩭䩮䩯䩰䩱䩲䩳䩴䩵䩶䩷䩸䩹䩺䩻䩼䩽䩾䩿 +䪀䪁䪂䪃䪄䪅䪆䪇䪈䪉䪊䪋䪌䪍䪎䪏䪐䪑䪒䪓䪔䪕䪖䪗䪘䪙䪚䪛䪜䪝䪞䪟 +䪠䪡䪢䪣䪤䪥䪦䪧䪨䪩䪪䪫䪬䪭䪮䪯䪰䪱䪲䪳䪴䪵䪶䪷䪸䪹䪺䪻䪼䪽䪾䪿 +䫀䫁䫂䫃䫄䫅䫆䫇䫈䫉䫊䫋䫌䫍䫎䫏䫐䫑䫒䫓䫔䫕䫖䫗䫘䫙䫚䫛䫜䫝䫞䫟 +䫠䫡䫢䫣䫤䫥䫦䫧䫨䫩䫪䫫䫬䫭䫮䫯䫰䫱䫲䫳䫴䫵䫶䫷䫸䫹䫺䫻䫼䫽䫾䫿 +䬀䬁䬂䬃䬄䬅䬆䬇䬈䬉䬊䬋䬌䬍䬎䬏䬐䬑䬒䬓䬔䬕䬖䬗䬘䬙䬚䬛䬜䬝䬞䬟 +䬠䬡䬢䬣䬤䬥䬦䬧䬨䬩䬪䬫䬬䬭䬮䬯䬰䬱䬲䬳䬴䬵䬶䬷䬸䬹䬺䬻䬼䬽䬾䬿 +䭀䭁䭂䭃䭄䭅䭆䭇䭈䭉䭊䭋䭌䭍䭎䭏䭐䭑䭒䭓䭔䭕䭖䭗䭘䭙䭚䭛䭜䭝䭞䭟 +䭠䭡䭢䭣䭤䭥䭦䭧䭨䭩䭪䭫䭬䭭䭮䭯䭰䭱䭲䭳䭴䭵䭶䭷䭸䭹䭺䭻䭼䭽䭾䭿 +䮀䮁䮂䮃䮄䮅䮆䮇䮈䮉䮊䮋䮌䮍䮎䮏䮐䮑䮒䮓䮔䮕䮖䮗䮘䮙䮚䮛䮜䮝䮞䮟 +䮠䮡䮢䮣䮤䮥䮦䮧䮨䮩䮪䮫䮬䮭䮮䮯䮰䮱䮲䮳䮴䮵䮶䮷䮸䮹䮺䮻䮼䮽䮾䮿 +䯀䯁䯂䯃䯄䯅䯆䯇䯈䯉䯊䯋䯌䯍䯎䯏䯐䯑䯒䯓䯔䯕䯖䯗䯘䯙䯚䯛䯜䯝䯞䯟 +䯠䯡䯢䯣䯤䯥䯦䯧䯨䯩䯪䯫䯬䯭䯮䯯䯰䯱䯲䯳䯴䯵䯶䯷䯸䯹䯺䯻䯼䯽䯾䯿 +䰀䰁䰂䰃䰄䰅䰆䰇䰈䰉䰊䰋䰌䰍䰎䰏䰐䰑䰒䰓䰔䰕䰖䰗䰘䰙䰚䰛䰜䰝䰞䰟 +䰠䰡䰢䰣䰤䰥䰦䰧䰨䰩䰪䰫䰬䰭䰮䰯䰰䰱䰲䰳䰴䰵䰶䰷䰸䰹䰺䰻䰼䰽䰾䰿 +䱀䱁䱂䱃䱄䱅䱆䱇䱈䱉䱊䱋䱌䱍䱎䱏䱐䱑䱒䱓䱔䱕䱖䱗䱘䱙䱚䱛䱜䱝䱞䱟 +䱠䱡䱢䱣䱤䱥䱦䱧䱨䱩䱪䱫䱬䱭䱮䱯䱰䱱䱲䱳䱴䱵䱶䱷䱸䱹䱺䱻䱼䱽䱾䱿 +䲀䲁䲂䲃䲄䲅䲆䲇䲈䲉䲊䲋䲌䲍䲎䲏䲐䲑䲒䲓䲔䲕䲖䲗䲘䲙䲚䲛䲜䲝䲞䲟 +䲠䲡䲢䲣䲤䲥䲦䲧䲨䲩䲪䲫䲬䲭䲮䲯䲰䲱䲲䲳䲴䲵䲶䲷䲸䲹䲺䲻䲼䲽䲾䲿 +䳀䳁䳂䳃䳄䳅䳆䳇䳈䳉䳊䳋䳌䳍䳎䳏䳐䳑䳒䳓䳔䳕䳖䳗䳘䳙䳚䳛䳜䳝䳞䳟 +䳠䳡䳢䳣䳤䳥䳦䳧䳨䳩䳪䳫䳬䳭䳮䳯䳰䳱䳲䳳䳴䳵䳶䳷䳸䳹䳺䳻䳼䳽䳾䳿 +䴀䴁䴂䴃䴄䴅䴆䴇䴈䴉䴊䴋䴌䴍䴎䴏䴐䴑䴒䴓䴔䴕䴖䴗䴘䴙䴚䴛䴜䴝䴞䴟 +䴠䴡䴢䴣䴤䴥䴦䴧䴨䴩䴪䴫䴬䴭䴮䴯䴰䴱䴲䴳䴴䴵䴶䴷䴸䴹䴺䴻䴼䴽䴾䴿 +䵀䵁䵂䵃䵄䵅䵆䵇䵈䵉䵊䵋䵌䵍䵎䵏䵐䵑䵒䵓䵔䵕䵖䵗䵘䵙䵚䵛䵜䵝䵞䵟 +䵠䵡䵢䵣䵤䵥䵦䵧䵨䵩䵪䵫䵬䵭䵮䵯䵰䵱䵲䵳䵴䵵䵶䵷䵸䵹䵺䵻䵼䵽䵾䵿 +䶀䶁䶂䶃䶄䶅䶆䶇䶈䶉䶊䶋䶌䶍䶎䶏䶐䶑䶒䶓䶔䶕䶖䶗䶘䶙䶚䶛䶜䶝䶞䶟 +䶠䶡䶢䶣䶤䶥䶦䶧䶨䶩䶪䶫䶬䶭䶮䶯䶰䶱䶲䶳䶴䶵䶶䶷䶸䶹䶺䶻䶼䶽䶾䶿 + +Yijing Hexagram Symbols (U+4DC0-U+4DFF): + +䷀䷁䷂䷃䷄䷅䷆䷇䷈䷉䷊䷋䷌䷍䷎䷏䷐䷑䷒䷓䷔䷕䷖䷗䷘䷙䷚䷛䷜䷝䷞䷟ +䷠䷡䷢䷣䷤䷥䷦䷧䷨䷩䷪䷫䷬䷭䷮䷯䷰䷱䷲䷳䷴䷵䷶䷷䷸䷹䷺䷻䷼䷽䷾䷿ + +CJK Unified Ideographs (U+4E00-U+9FFF): + +一丁丂七丄丅丆万丈三上下丌不与丏丐丑丒专且丕世丗丘丙业丛东丝丞丟 +丠両丢丣两严並丧丨丩个丫丬中丮丯丰丱串丳临丵丶丷丸丹为主丼丽举丿 +乀乁乂乃乄久乆乇么义乊之乌乍乎乏乐乑乒乓乔乕乖乗乘乙乚乛乜九乞也 +习乡乢乣乤乥书乧乨乩乪乫乬乭乮乯买乱乲乳乴乵乶乷乸乹乺乻乼乽乾乿 +亀亁亂亃亄亅了亇予争亊事二亍于亏亐云互亓五井亖亗亘亙亚些亜亝亞亟 +亠亡亢亣交亥亦产亨亩亪享京亭亮亯亰亱亲亳亴亵亶亷亸亹人亻亼亽亾亿 +什仁仂仃仄仅仆仇仈仉今介仌仍从仏仐仑仒仓仔仕他仗付仙仚仛仜仝仞仟 +仠仡仢代令以仦仧仨仩仪仫们仭仮仯仰仱仲仳仴仵件价仸仹仺任仼份仾仿 +伀企伂伃伄伅伆伇伈伉伊伋伌伍伎伏伐休伒伓伔伕伖众优伙会伛伜伝伞伟 +传伡伢伣伤伥伦伧伨伩伪伫伬伭伮伯估伱伲伳伴伵伶伷伸伹伺伻似伽伾伿 +佀佁佂佃佄佅但佇佈佉佊佋佌位低住佐佑佒体佔何佖佗佘余佚佛作佝佞佟 +你佡佢佣佤佥佦佧佨佩佪佫佬佭佮佯佰佱佲佳佴併佶佷佸佹佺佻佼佽佾使 +侀侁侂侃侄侅來侇侈侉侊例侌侍侎侏侐侑侒侓侔侕侖侗侘侙侚供侜依侞侟 +侠価侢侣侤侥侦侧侨侩侪侫侬侭侮侯侰侱侲侳侴侵侶侷侸侹侺侻侼侽侾便 +俀俁係促俄俅俆俇俈俉俊俋俌俍俎俏俐俑俒俓俔俕俖俗俘俙俚俛俜保俞俟 +俠信俢俣俤俥俦俧俨俩俪俫俬俭修俯俰俱俲俳俴俵俶俷俸俹俺俻俼俽俾俿 +倀倁倂倃倄倅倆倇倈倉倊個倌倍倎倏倐們倒倓倔倕倖倗倘候倚倛倜倝倞借 +倠倡倢倣値倥倦倧倨倩倪倫倬倭倮倯倰倱倲倳倴倵倶倷倸倹债倻值倽倾倿 +偀偁偂偃偄偅偆假偈偉偊偋偌偍偎偏偐偑偒偓偔偕偖偗偘偙做偛停偝偞偟 +偠偡偢偣偤健偦偧偨偩偪偫偬偭偮偯偰偱偲偳側偵偶偷偸偹偺偻偼偽偾偿 +傀傁傂傃傄傅傆傇傈傉傊傋傌傍傎傏傐傑傒傓傔傕傖傗傘備傚傛傜傝傞傟 +傠傡傢傣傤傥傦傧储傩傪傫催傭傮傯傰傱傲傳傴債傶傷傸傹傺傻傼傽傾傿 +僀僁僂僃僄僅僆僇僈僉僊僋僌働僎像僐僑僒僓僔僕僖僗僘僙僚僛僜僝僞僟 +僠僡僢僣僤僥僦僧僨僩僪僫僬僭僮僯僰僱僲僳僴僵僶僷僸價僺僻僼僽僾僿 +儀儁儂儃億儅儆儇儈儉儊儋儌儍儎儏儐儑儒儓儔儕儖儗儘儙儚儛儜儝儞償 +儠儡儢儣儤儥儦儧儨儩優儫儬儭儮儯儰儱儲儳儴儵儶儷儸儹儺儻儼儽儾儿 +兀允兂元兄充兆兇先光兊克兌免兎兏児兑兒兓兔兕兖兗兘兙党兛兜兝兞兟 +兠兡兢兣兤入兦內全兩兪八公六兮兯兰共兲关兴兵其具典兹兺养兼兽兾兿 +冀冁冂冃冄内円冇冈冉冊冋册再冎冏冐冑冒冓冔冕冖冗冘写冚军农冝冞冟 +冠冡冢冣冤冥冦冧冨冩冪冫冬冭冮冯冰冱冲决冴况冶冷冸冹冺冻冼冽冾冿 +净凁凂凃凄凅准凇凈凉凊凋凌凍凎减凐凑凒凓凔凕凖凗凘凙凚凛凜凝凞凟 +几凡凢凣凤凥処凧凨凩凪凫凬凭凮凯凰凱凲凳凴凵凶凷凸凹出击凼函凾凿 +刀刁刂刃刄刅分切刈刉刊刋刌刍刎刏刐刑划刓刔刕刖列刘则刚创刜初刞刟 +删刡刢刣判別刦刧刨利刪别刬刭刮刯到刱刲刳刴刵制刷券刹刺刻刼刽刾刿 +剀剁剂剃剄剅剆則剈剉削剋剌前剎剏剐剑剒剓剔剕剖剗剘剙剚剛剜剝剞剟 +剠剡剢剣剤剥剦剧剨剩剪剫剬剭剮副剰剱割剳剴創剶剷剸剹剺剻剼剽剾剿 +劀劁劂劃劄劅劆劇劈劉劊劋劌劍劎劏劐劑劒劓劔劕劖劗劘劙劚力劜劝办功 +加务劢劣劤劥劦劧动助努劫劬劭劮劯劰励劲劳労劵劶劷劸効劺劻劼劽劾势 +勀勁勂勃勄勅勆勇勈勉勊勋勌勍勎勏勐勑勒勓勔動勖勗勘務勚勛勜勝勞募 +勠勡勢勣勤勥勦勧勨勩勪勫勬勭勮勯勰勱勲勳勴勵勶勷勸勹勺勻勼勽勾勿 +匀匁匂匃匄包匆匇匈匉匊匋匌匍匎匏匐匑匒匓匔匕化北匘匙匚匛匜匝匞匟 +匠匡匢匣匤匥匦匧匨匩匪匫匬匭匮匯匰匱匲匳匴匵匶匷匸匹区医匼匽匾匿 +區十卂千卄卅卆升午卉半卋卌卍华协卐卑卒卓協单卖南単卙博卛卜卝卞卟 +占卡卢卣卤卥卦卧卨卩卪卫卬卭卮卯印危卲即却卵卶卷卸卹卺卻卼卽卾卿 +厀厁厂厃厄厅历厇厈厉厊压厌厍厎厏厐厑厒厓厔厕厖厗厘厙厚厛厜厝厞原 +厠厡厢厣厤厥厦厧厨厩厪厫厬厭厮厯厰厱厲厳厴厵厶厷厸厹厺去厼厽厾县 +叀叁参參叄叅叆叇又叉及友双反収叏叐发叒叓叔叕取受变叙叚叛叜叝叞叟 +叠叡叢口古句另叧叨叩只叫召叭叮可台叱史右叴叵叶号司叹叺叻叼叽叾叿 +吀吁吂吃各吅吆吇合吉吊吋同名后吏吐向吒吓吔吕吖吗吘吙吚君吜吝吞吟 +吠吡吢吣吤吥否吧吨吩吪含听吭吮启吰吱吲吳吴吵吶吷吸吹吺吻吼吽吾吿 +呀呁呂呃呄呅呆呇呈呉告呋呌呍呎呏呐呑呒呓呔呕呖呗员呙呚呛呜呝呞呟 +呠呡呢呣呤呥呦呧周呩呪呫呬呭呮呯呰呱呲味呴呵呶呷呸呹呺呻呼命呾呿 +咀咁咂咃咄咅咆咇咈咉咊咋和咍咎咏咐咑咒咓咔咕咖咗咘咙咚咛咜咝咞咟 +咠咡咢咣咤咥咦咧咨咩咪咫咬咭咮咯咰咱咲咳咴咵咶咷咸咹咺咻咼咽咾咿 +哀品哂哃哄哅哆哇哈哉哊哋哌响哎哏哐哑哒哓哔哕哖哗哘哙哚哛哜哝哞哟 +哠員哢哣哤哥哦哧哨哩哪哫哬哭哮哯哰哱哲哳哴哵哶哷哸哹哺哻哼哽哾哿 +唀唁唂唃唄唅唆唇唈唉唊唋唌唍唎唏唐唑唒唓唔唕唖唗唘唙唚唛唜唝唞唟 +唠唡唢唣唤唥唦唧唨唩唪唫唬唭售唯唰唱唲唳唴唵唶唷唸唹唺唻唼唽唾唿 +啀啁啂啃啄啅商啇啈啉啊啋啌啍啎問啐啑啒啓啔啕啖啗啘啙啚啛啜啝啞啟 +啠啡啢啣啤啥啦啧啨啩啪啫啬啭啮啯啰啱啲啳啴啵啶啷啸啹啺啻啼啽啾啿 +喀喁喂喃善喅喆喇喈喉喊喋喌喍喎喏喐喑喒喓喔喕喖喗喘喙喚喛喜喝喞喟 +喠喡喢喣喤喥喦喧喨喩喪喫喬喭單喯喰喱喲喳喴喵営喷喸喹喺喻喼喽喾喿 +嗀嗁嗂嗃嗄嗅嗆嗇嗈嗉嗊嗋嗌嗍嗎嗏嗐嗑嗒嗓嗔嗕嗖嗗嗘嗙嗚嗛嗜嗝嗞嗟 +嗠嗡嗢嗣嗤嗥嗦嗧嗨嗩嗪嗫嗬嗭嗮嗯嗰嗱嗲嗳嗴嗵嗶嗷嗸嗹嗺嗻嗼嗽嗾嗿 +嘀嘁嘂嘃嘄嘅嘆嘇嘈嘉嘊嘋嘌嘍嘎嘏嘐嘑嘒嘓嘔嘕嘖嘗嘘嘙嘚嘛嘜嘝嘞嘟 +嘠嘡嘢嘣嘤嘥嘦嘧嘨嘩嘪嘫嘬嘭嘮嘯嘰嘱嘲嘳嘴嘵嘶嘷嘸嘹嘺嘻嘼嘽嘾嘿 +噀噁噂噃噄噅噆噇噈噉噊噋噌噍噎噏噐噑噒噓噔噕噖噗噘噙噚噛噜噝噞噟 +噠噡噢噣噤噥噦噧器噩噪噫噬噭噮噯噰噱噲噳噴噵噶噷噸噹噺噻噼噽噾噿 +嚀嚁嚂嚃嚄嚅嚆嚇嚈嚉嚊嚋嚌嚍嚎嚏嚐嚑嚒嚓嚔嚕嚖嚗嚘嚙嚚嚛嚜嚝嚞嚟 +嚠嚡嚢嚣嚤嚥嚦嚧嚨嚩嚪嚫嚬嚭嚮嚯嚰嚱嚲嚳嚴嚵嚶嚷嚸嚹嚺嚻嚼嚽嚾嚿 +囀囁囂囃囄囅囆囇囈囉囊囋囌囍囎囏囐囑囒囓囔囕囖囗囘囙囚四囜囝回囟 +因囡团団囤囥囦囧囨囩囪囫囬园囮囯困囱囲図围囵囶囷囸囹固囻囼国图囿 +圀圁圂圃圄圅圆圇圈圉圊國圌圍圎圏圐圑園圓圔圕圖圗團圙圚圛圜圝圞土 +圠圡圢圣圤圥圦圧在圩圪圫圬圭圮圯地圱圲圳圴圵圶圷圸圹场圻圼圽圾圿 +址坁坂坃坄坅坆均坈坉坊坋坌坍坎坏坐坑坒坓坔坕坖块坘坙坚坛坜坝坞坟 +坠坡坢坣坤坥坦坧坨坩坪坫坬坭坮坯坰坱坲坳坴坵坶坷坸坹坺坻坼坽坾坿 +垀垁垂垃垄垅垆垇垈垉垊型垌垍垎垏垐垑垒垓垔垕垖垗垘垙垚垛垜垝垞垟 +垠垡垢垣垤垥垦垧垨垩垪垫垬垭垮垯垰垱垲垳垴垵垶垷垸垹垺垻垼垽垾垿 +埀埁埂埃埄埅埆埇埈埉埊埋埌埍城埏埐埑埒埓埔埕埖埗埘埙埚埛埜埝埞域 +埠埡埢埣埤埥埦埧埨埩埪埫埬埭埮埯埰埱埲埳埴埵埶執埸培基埻埼埽埾埿 +堀堁堂堃堄堅堆堇堈堉堊堋堌堍堎堏堐堑堒堓堔堕堖堗堘堙堚堛堜堝堞堟 +堠堡堢堣堤堥堦堧堨堩堪堫堬堭堮堯堰報堲堳場堵堶堷堸堹堺堻堼堽堾堿 +塀塁塂塃塄塅塆塇塈塉塊塋塌塍塎塏塐塑塒塓塔塕塖塗塘塙塚塛塜塝塞塟 +塠塡塢塣塤塥塦塧塨塩塪填塬塭塮塯塰塱塲塳塴塵塶塷塸塹塺塻塼塽塾塿 +墀墁墂境墄墅墆墇墈墉墊墋墌墍墎墏墐墑墒墓墔墕墖増墘墙墚墛墜墝增墟 +墠墡墢墣墤墥墦墧墨墩墪墫墬墭墮墯墰墱墲墳墴墵墶墷墸墹墺墻墼墽墾墿 +壀壁壂壃壄壅壆壇壈壉壊壋壌壍壎壏壐壑壒壓壔壕壖壗壘壙壚壛壜壝壞壟 +壠壡壢壣壤壥壦壧壨壩壪士壬壭壮壯声壱売壳壴壵壶壷壸壹壺壻壼壽壾壿 +夀夁夂夃处夅夆备夈変夊夋夌复夎夏夐夑夒夓夔夕外夗夘夙多夛夜夝夞够 +夠夡夢夣夤夥夦大夨天太夫夬夭央夯夰失夲夳头夵夶夷夸夹夺夻夼夽夾夿 +奀奁奂奃奄奅奆奇奈奉奊奋奌奍奎奏奐契奒奓奔奕奖套奘奙奚奛奜奝奞奟 +奠奡奢奣奤奥奦奧奨奩奪奫奬奭奮奯奰奱奲女奴奵奶奷奸她奺奻奼好奾奿 +妀妁如妃妄妅妆妇妈妉妊妋妌妍妎妏妐妑妒妓妔妕妖妗妘妙妚妛妜妝妞妟 +妠妡妢妣妤妥妦妧妨妩妪妫妬妭妮妯妰妱妲妳妴妵妶妷妸妹妺妻妼妽妾妿 +姀姁姂姃姄姅姆姇姈姉姊始姌姍姎姏姐姑姒姓委姕姖姗姘姙姚姛姜姝姞姟 +姠姡姢姣姤姥姦姧姨姩姪姫姬姭姮姯姰姱姲姳姴姵姶姷姸姹姺姻姼姽姾姿 +娀威娂娃娄娅娆娇娈娉娊娋娌娍娎娏娐娑娒娓娔娕娖娗娘娙娚娛娜娝娞娟 +娠娡娢娣娤娥娦娧娨娩娪娫娬娭娮娯娰娱娲娳娴娵娶娷娸娹娺娻娼娽娾娿 +婀婁婂婃婄婅婆婇婈婉婊婋婌婍婎婏婐婑婒婓婔婕婖婗婘婙婚婛婜婝婞婟 +婠婡婢婣婤婥婦婧婨婩婪婫婬婭婮婯婰婱婲婳婴婵婶婷婸婹婺婻婼婽婾婿 +媀媁媂媃媄媅媆媇媈媉媊媋媌媍媎媏媐媑媒媓媔媕媖媗媘媙媚媛媜媝媞媟 +媠媡媢媣媤媥媦媧媨媩媪媫媬媭媮媯媰媱媲媳媴媵媶媷媸媹媺媻媼媽媾媿 +嫀嫁嫂嫃嫄嫅嫆嫇嫈嫉嫊嫋嫌嫍嫎嫏嫐嫑嫒嫓嫔嫕嫖嫗嫘嫙嫚嫛嫜嫝嫞嫟 +嫠嫡嫢嫣嫤嫥嫦嫧嫨嫩嫪嫫嫬嫭嫮嫯嫰嫱嫲嫳嫴嫵嫶嫷嫸嫹嫺嫻嫼嫽嫾嫿 +嬀嬁嬂嬃嬄嬅嬆嬇嬈嬉嬊嬋嬌嬍嬎嬏嬐嬑嬒嬓嬔嬕嬖嬗嬘嬙嬚嬛嬜嬝嬞嬟 +嬠嬡嬢嬣嬤嬥嬦嬧嬨嬩嬪嬫嬬嬭嬮嬯嬰嬱嬲嬳嬴嬵嬶嬷嬸嬹嬺嬻嬼嬽嬾嬿 +孀孁孂孃孄孅孆孇孈孉孊孋孌孍孎孏子孑孒孓孔孕孖字存孙孚孛孜孝孞孟 +孠孡孢季孤孥学孧孨孩孪孫孬孭孮孯孰孱孲孳孴孵孶孷學孹孺孻孼孽孾孿 +宀宁宂它宄宅宆宇守安宊宋完宍宎宏宐宑宒宓宔宕宖宗官宙定宛宜宝实実 +宠审客宣室宥宦宧宨宩宪宫宬宭宮宯宰宱宲害宴宵家宷宸容宺宻宼宽宾宿 +寀寁寂寃寄寅密寇寈寉寊寋富寍寎寏寐寑寒寓寔寕寖寗寘寙寚寛寜寝寞察 +寠寡寢寣寤寥實寧寨審寪寫寬寭寮寯寰寱寲寳寴寵寶寷寸对寺寻导寽対寿 +尀封専尃射尅将將專尉尊尋尌對導小尐少尒尓尔尕尖尗尘尙尚尛尜尝尞尟 +尠尡尢尣尤尥尦尧尨尩尪尫尬尭尮尯尰就尲尳尴尵尶尷尸尹尺尻尼尽尾尿 +局屁层屃屄居屆屇屈屉届屋屌屍屎屏屐屑屒屓屔展屖屗屘屙屚屛屜屝属屟 +屠屡屢屣層履屦屧屨屩屪屫屬屭屮屯屰山屲屳屴屵屶屷屸屹屺屻屼屽屾屿 +岀岁岂岃岄岅岆岇岈岉岊岋岌岍岎岏岐岑岒岓岔岕岖岗岘岙岚岛岜岝岞岟 +岠岡岢岣岤岥岦岧岨岩岪岫岬岭岮岯岰岱岲岳岴岵岶岷岸岹岺岻岼岽岾岿 +峀峁峂峃峄峅峆峇峈峉峊峋峌峍峎峏峐峑峒峓峔峕峖峗峘峙峚峛峜峝峞峟 +峠峡峢峣峤峥峦峧峨峩峪峫峬峭峮峯峰峱峲峳峴峵島峷峸峹峺峻峼峽峾峿 +崀崁崂崃崄崅崆崇崈崉崊崋崌崍崎崏崐崑崒崓崔崕崖崗崘崙崚崛崜崝崞崟 +崠崡崢崣崤崥崦崧崨崩崪崫崬崭崮崯崰崱崲崳崴崵崶崷崸崹崺崻崼崽崾崿 +嵀嵁嵂嵃嵄嵅嵆嵇嵈嵉嵊嵋嵌嵍嵎嵏嵐嵑嵒嵓嵔嵕嵖嵗嵘嵙嵚嵛嵜嵝嵞嵟 +嵠嵡嵢嵣嵤嵥嵦嵧嵨嵩嵪嵫嵬嵭嵮嵯嵰嵱嵲嵳嵴嵵嵶嵷嵸嵹嵺嵻嵼嵽嵾嵿 +嶀嶁嶂嶃嶄嶅嶆嶇嶈嶉嶊嶋嶌嶍嶎嶏嶐嶑嶒嶓嶔嶕嶖嶗嶘嶙嶚嶛嶜嶝嶞嶟 +嶠嶡嶢嶣嶤嶥嶦嶧嶨嶩嶪嶫嶬嶭嶮嶯嶰嶱嶲嶳嶴嶵嶶嶷嶸嶹嶺嶻嶼嶽嶾嶿 +巀巁巂巃巄巅巆巇巈巉巊巋巌巍巎巏巐巑巒巓巔巕巖巗巘巙巚巛巜川州巟 +巠巡巢巣巤工左巧巨巩巪巫巬巭差巯巰己已巳巴巵巶巷巸巹巺巻巼巽巾巿 +帀币市布帄帅帆帇师帉帊帋希帍帎帏帐帑帒帓帔帕帖帗帘帙帚帛帜帝帞帟 +帠帡帢帣帤帥带帧帨帩帪師帬席帮帯帰帱帲帳帴帵帶帷常帹帺帻帼帽帾帿 +幀幁幂幃幄幅幆幇幈幉幊幋幌幍幎幏幐幑幒幓幔幕幖幗幘幙幚幛幜幝幞幟 +幠幡幢幣幤幥幦幧幨幩幪幫幬幭幮幯幰幱干平年幵并幷幸幹幺幻幼幽幾广 +庀庁庂広庄庅庆庇庈庉床庋庌庍庎序庐庑庒库应底庖店庘庙庚庛府庝庞废 +庠庡庢庣庤庥度座庨庩庪庫庬庭庮庯庰庱庲庳庴庵庶康庸庹庺庻庼庽庾庿 +廀廁廂廃廄廅廆廇廈廉廊廋廌廍廎廏廐廑廒廓廔廕廖廗廘廙廚廛廜廝廞廟 +廠廡廢廣廤廥廦廧廨廩廪廫廬廭廮廯廰廱廲廳廴廵延廷廸廹建廻廼廽廾廿 +开弁异弃弄弅弆弇弈弉弊弋弌弍弎式弐弑弒弓弔引弖弗弘弙弚弛弜弝弞弟 +张弡弢弣弤弥弦弧弨弩弪弫弬弭弮弯弰弱弲弳弴張弶強弸弹强弻弼弽弾弿 +彀彁彂彃彄彅彆彇彈彉彊彋彌彍彎彏彐彑归当彔录彖彗彘彙彚彛彜彝彞彟 +彠彡形彣彤彥彦彧彨彩彪彫彬彭彮彯彰影彲彳彴彵彶彷彸役彺彻彼彽彾彿 +往征徂徃径待徆徇很徉徊律後徍徎徏徐徑徒従徔徕徖得徘徙徚徛徜徝從徟 +徠御徢徣徤徥徦徧徨復循徫徬徭微徯徰徱徲徳徴徵徶德徸徹徺徻徼徽徾徿 +忀忁忂心忄必忆忇忈忉忊忋忌忍忎忏忐忑忒忓忔忕忖志忘忙忚忛応忝忞忟 +忠忡忢忣忤忥忦忧忨忩忪快忬忭忮忯忰忱忲忳忴念忶忷忸忹忺忻忼忽忾忿 +怀态怂怃怄怅怆怇怈怉怊怋怌怍怎怏怐怑怒怓怔怕怖怗怘怙怚怛怜思怞怟 +怠怡怢怣怤急怦性怨怩怪怫怬怭怮怯怰怱怲怳怴怵怶怷怸怹怺总怼怽怾怿 +恀恁恂恃恄恅恆恇恈恉恊恋恌恍恎恏恐恑恒恓恔恕恖恗恘恙恚恛恜恝恞恟 +恠恡恢恣恤恥恦恧恨恩恪恫恬恭恮息恰恱恲恳恴恵恶恷恸恹恺恻恼恽恾恿 +悀悁悂悃悄悅悆悇悈悉悊悋悌悍悎悏悐悑悒悓悔悕悖悗悘悙悚悛悜悝悞悟 +悠悡悢患悤悥悦悧您悩悪悫悬悭悮悯悰悱悲悳悴悵悶悷悸悹悺悻悼悽悾悿 +惀惁惂惃惄情惆惇惈惉惊惋惌惍惎惏惐惑惒惓惔惕惖惗惘惙惚惛惜惝惞惟 +惠惡惢惣惤惥惦惧惨惩惪惫惬惭惮惯惰惱惲想惴惵惶惷惸惹惺惻惼惽惾惿 +愀愁愂愃愄愅愆愇愈愉愊愋愌愍愎意愐愑愒愓愔愕愖愗愘愙愚愛愜愝愞感 +愠愡愢愣愤愥愦愧愨愩愪愫愬愭愮愯愰愱愲愳愴愵愶愷愸愹愺愻愼愽愾愿 +慀慁慂慃慄慅慆慇慈慉慊態慌慍慎慏慐慑慒慓慔慕慖慗慘慙慚慛慜慝慞慟 +慠慡慢慣慤慥慦慧慨慩慪慫慬慭慮慯慰慱慲慳慴慵慶慷慸慹慺慻慼慽慾慿 +憀憁憂憃憄憅憆憇憈憉憊憋憌憍憎憏憐憑憒憓憔憕憖憗憘憙憚憛憜憝憞憟 +憠憡憢憣憤憥憦憧憨憩憪憫憬憭憮憯憰憱憲憳憴憵憶憷憸憹憺憻憼憽憾憿 +懀懁懂懃懄懅懆懇懈應懊懋懌懍懎懏懐懑懒懓懔懕懖懗懘懙懚懛懜懝懞懟 +懠懡懢懣懤懥懦懧懨懩懪懫懬懭懮懯懰懱懲懳懴懵懶懷懸懹懺懻懼懽懾懿 +戀戁戂戃戄戅戆戇戈戉戊戋戌戍戎戏成我戒戓戔戕或戗战戙戚戛戜戝戞戟 +戠戡戢戣戤戥戦戧戨戩截戫戬戭戮戯戰戱戲戳戴戵戶户戸戹戺戻戼戽戾房 +所扁扂扃扄扅扆扇扈扉扊手扌才扎扏扐扑扒打扔払扖扗托扙扚扛扜扝扞扟 +扠扡扢扣扤扥扦执扨扩扪扫扬扭扮扯扰扱扲扳扴扵扶扷扸批扺扻扼扽找承 +技抁抂抃抄抅抆抇抈抉把抋抌抍抎抏抐抑抒抓抔投抖抗折抙抚抛抜抝択抟 +抠抡抢抣护报抦抧抨抩抪披抬抭抮抯抰抱抲抳抴抵抶抷抸抹抺抻押抽抾抿 +拀拁拂拃拄担拆拇拈拉拊拋拌拍拎拏拐拑拒拓拔拕拖拗拘拙拚招拜拝拞拟 +拠拡拢拣拤拥拦拧拨择拪拫括拭拮拯拰拱拲拳拴拵拶拷拸拹拺拻拼拽拾拿 +挀持挂挃挄挅挆指挈按挊挋挌挍挎挏挐挑挒挓挔挕挖挗挘挙挚挛挜挝挞挟 +挠挡挢挣挤挥挦挧挨挩挪挫挬挭挮振挰挱挲挳挴挵挶挷挸挹挺挻挼挽挾挿 +捀捁捂捃捄捅捆捇捈捉捊捋捌捍捎捏捐捑捒捓捔捕捖捗捘捙捚捛捜捝捞损 +捠捡换捣捤捥捦捧捨捩捪捫捬捭据捯捰捱捲捳捴捵捶捷捸捹捺捻捼捽捾捿 +掀掁掂掃掄掅掆掇授掉掊掋掌掍掎掏掐掑排掓掔掕掖掗掘掙掚掛掜掝掞掟 +掠採探掣掤接掦控推掩措掫掬掭掮掯掰掱掲掳掴掵掶掷掸掹掺掻掼掽掾掿 +揀揁揂揃揄揅揆揇揈揉揊揋揌揍揎描提揑插揓揔揕揖揗揘揙揚換揜揝揞揟 +揠握揢揣揤揥揦揧揨揩揪揫揬揭揮揯揰揱揲揳援揵揶揷揸揹揺揻揼揽揾揿 +搀搁搂搃搄搅搆搇搈搉搊搋搌損搎搏搐搑搒搓搔搕搖搗搘搙搚搛搜搝搞搟 +搠搡搢搣搤搥搦搧搨搩搪搫搬搭搮搯搰搱搲搳搴搵搶搷搸搹携搻搼搽搾搿 +摀摁摂摃摄摅摆摇摈摉摊摋摌摍摎摏摐摑摒摓摔摕摖摗摘摙摚摛摜摝摞摟 +摠摡摢摣摤摥摦摧摨摩摪摫摬摭摮摯摰摱摲摳摴摵摶摷摸摹摺摻摼摽摾摿 +撀撁撂撃撄撅撆撇撈撉撊撋撌撍撎撏撐撑撒撓撔撕撖撗撘撙撚撛撜撝撞撟 +撠撡撢撣撤撥撦撧撨撩撪撫撬播撮撯撰撱撲撳撴撵撶撷撸撹撺撻撼撽撾撿 +擀擁擂擃擄擅擆擇擈擉擊擋擌操擎擏擐擑擒擓擔擕擖擗擘擙據擛擜擝擞擟 +擠擡擢擣擤擥擦擧擨擩擪擫擬擭擮擯擰擱擲擳擴擵擶擷擸擹擺擻擼擽擾擿 +攀攁攂攃攄攅攆攇攈攉攊攋攌攍攎攏攐攑攒攓攔攕攖攗攘攙攚攛攜攝攞攟 +攠攡攢攣攤攥攦攧攨攩攪攫攬攭攮支攰攱攲攳攴攵收攷攸改攺攻攼攽放政 +敀敁敂敃敄故敆敇效敉敊敋敌敍敎敏敐救敒敓敔敕敖敗敘教敚敛敜敝敞敟 +敠敡敢散敤敥敦敧敨敩敪敫敬敭敮敯数敱敲敳整敵敶敷數敹敺敻敼敽敾敿 +斀斁斂斃斄斅斆文斈斉斊斋斌斍斎斏斐斑斒斓斔斕斖斗斘料斚斛斜斝斞斟 +斠斡斢斣斤斥斦斧斨斩斪斫斬断斮斯新斱斲斳斴斵斶斷斸方斺斻於施斾斿 +旀旁旂旃旄旅旆旇旈旉旊旋旌旍旎族旐旑旒旓旔旕旖旗旘旙旚旛旜旝旞旟 +无旡既旣旤日旦旧旨早旪旫旬旭旮旯旰旱旲旳旴旵时旷旸旹旺旻旼旽旾旿 +昀昁昂昃昄昅昆昇昈昉昊昋昌昍明昏昐昑昒易昔昕昖昗昘昙昚昛昜昝昞星 +映昡昢昣昤春昦昧昨昩昪昫昬昭昮是昰昱昲昳昴昵昶昷昸昹昺昻昼昽显昿 +晀晁時晃晄晅晆晇晈晉晊晋晌晍晎晏晐晑晒晓晔晕晖晗晘晙晚晛晜晝晞晟 +晠晡晢晣晤晥晦晧晨晩晪晫晬晭普景晰晱晲晳晴晵晶晷晸晹智晻晼晽晾晿 +暀暁暂暃暄暅暆暇暈暉暊暋暌暍暎暏暐暑暒暓暔暕暖暗暘暙暚暛暜暝暞暟 +暠暡暢暣暤暥暦暧暨暩暪暫暬暭暮暯暰暱暲暳暴暵暶暷暸暹暺暻暼暽暾暿 +曀曁曂曃曄曅曆曇曈曉曊曋曌曍曎曏曐曑曒曓曔曕曖曗曘曙曚曛曜曝曞曟 +曠曡曢曣曤曥曦曧曨曩曪曫曬曭曮曯曰曱曲曳更曵曶曷書曹曺曻曼曽曾替 +最朁朂會朄朅朆朇月有朊朋朌服朎朏朐朑朒朓朔朕朖朗朘朙朚望朜朝朞期 +朠朡朢朣朤朥朦朧木朩未末本札朮术朰朱朲朳朴朵朶朷朸朹机朻朼朽朾朿 +杀杁杂权杄杅杆杇杈杉杊杋杌杍李杏材村杒杓杔杕杖杗杘杙杚杛杜杝杞束 +杠条杢杣杤来杦杧杨杩杪杫杬杭杮杯杰東杲杳杴杵杶杷杸杹杺杻杼杽松板 +枀极枂枃构枅枆枇枈枉枊枋枌枍枎枏析枑枒枓枔枕枖林枘枙枚枛果枝枞枟 +枠枡枢枣枤枥枦枧枨枩枪枫枬枭枮枯枰枱枲枳枴枵架枷枸枹枺枻枼枽枾枿 +柀柁柂柃柄柅柆柇柈柉柊柋柌柍柎柏某柑柒染柔柕柖柗柘柙柚柛柜柝柞柟 +柠柡柢柣柤查柦柧柨柩柪柫柬柭柮柯柰柱柲柳柴柵柶柷柸柹柺査柼柽柾柿 +栀栁栂栃栄栅栆标栈栉栊栋栌栍栎栏栐树栒栓栔栕栖栗栘栙栚栛栜栝栞栟 +栠校栢栣栤栥栦栧栨栩株栫栬栭栮栯栰栱栲栳栴栵栶样核根栺栻格栽栾栿 +桀桁桂桃桄桅框桇案桉桊桋桌桍桎桏桐桑桒桓桔桕桖桗桘桙桚桛桜桝桞桟 +桠桡桢档桤桥桦桧桨桩桪桫桬桭桮桯桰桱桲桳桴桵桶桷桸桹桺桻桼桽桾桿 +梀梁梂梃梄梅梆梇梈梉梊梋梌梍梎梏梐梑梒梓梔梕梖梗梘梙梚梛梜條梞梟 +梠梡梢梣梤梥梦梧梨梩梪梫梬梭梮梯械梱梲梳梴梵梶梷梸梹梺梻梼梽梾梿 +检棁棂棃棄棅棆棇棈棉棊棋棌棍棎棏棐棑棒棓棔棕棖棗棘棙棚棛棜棝棞棟 +棠棡棢棣棤棥棦棧棨棩棪棫棬棭森棯棰棱棲棳棴棵棶棷棸棹棺棻棼棽棾棿 +椀椁椂椃椄椅椆椇椈椉椊椋椌植椎椏椐椑椒椓椔椕椖椗椘椙椚椛検椝椞椟 +椠椡椢椣椤椥椦椧椨椩椪椫椬椭椮椯椰椱椲椳椴椵椶椷椸椹椺椻椼椽椾椿 +楀楁楂楃楄楅楆楇楈楉楊楋楌楍楎楏楐楑楒楓楔楕楖楗楘楙楚楛楜楝楞楟 +楠楡楢楣楤楥楦楧楨楩楪楫楬業楮楯楰楱楲楳楴極楶楷楸楹楺楻楼楽楾楿 +榀榁概榃榄榅榆榇榈榉榊榋榌榍榎榏榐榑榒榓榔榕榖榗榘榙榚榛榜榝榞榟 +榠榡榢榣榤榥榦榧榨榩榪榫榬榭榮榯榰榱榲榳榴榵榶榷榸榹榺榻榼榽榾榿 +槀槁槂槃槄槅槆槇槈槉槊構槌槍槎槏槐槑槒槓槔槕槖槗様槙槚槛槜槝槞槟 +槠槡槢槣槤槥槦槧槨槩槪槫槬槭槮槯槰槱槲槳槴槵槶槷槸槹槺槻槼槽槾槿 +樀樁樂樃樄樅樆樇樈樉樊樋樌樍樎樏樐樑樒樓樔樕樖樗樘標樚樛樜樝樞樟 +樠模樢樣樤樥樦樧樨権横樫樬樭樮樯樰樱樲樳樴樵樶樷樸樹樺樻樼樽樾樿 +橀橁橂橃橄橅橆橇橈橉橊橋橌橍橎橏橐橑橒橓橔橕橖橗橘橙橚橛橜橝橞機 +橠橡橢橣橤橥橦橧橨橩橪橫橬橭橮橯橰橱橲橳橴橵橶橷橸橹橺橻橼橽橾橿 +檀檁檂檃檄檅檆檇檈檉檊檋檌檍檎檏檐檑檒檓檔檕檖檗檘檙檚檛檜檝檞檟 +檠檡檢檣檤檥檦檧檨檩檪檫檬檭檮檯檰檱檲檳檴檵檶檷檸檹檺檻檼檽檾檿 +櫀櫁櫂櫃櫄櫅櫆櫇櫈櫉櫊櫋櫌櫍櫎櫏櫐櫑櫒櫓櫔櫕櫖櫗櫘櫙櫚櫛櫜櫝櫞櫟 +櫠櫡櫢櫣櫤櫥櫦櫧櫨櫩櫪櫫櫬櫭櫮櫯櫰櫱櫲櫳櫴櫵櫶櫷櫸櫹櫺櫻櫼櫽櫾櫿 +欀欁欂欃欄欅欆欇欈欉權欋欌欍欎欏欐欑欒欓欔欕欖欗欘欙欚欛欜欝欞欟 +欠次欢欣欤欥欦欧欨欩欪欫欬欭欮欯欰欱欲欳欴欵欶欷欸欹欺欻欼欽款欿 +歀歁歂歃歄歅歆歇歈歉歊歋歌歍歎歏歐歑歒歓歔歕歖歗歘歙歚歛歜歝歞歟 +歠歡止正此步武歧歨歩歪歫歬歭歮歯歰歱歲歳歴歵歶歷歸歹歺死歼歽歾歿 +殀殁殂殃殄殅殆殇殈殉殊残殌殍殎殏殐殑殒殓殔殕殖殗殘殙殚殛殜殝殞殟 +殠殡殢殣殤殥殦殧殨殩殪殫殬殭殮殯殰殱殲殳殴段殶殷殸殹殺殻殼殽殾殿 +毀毁毂毃毄毅毆毇毈毉毊毋毌母毎每毐毑毒毓比毕毖毗毘毙毚毛毜毝毞毟 +毠毡毢毣毤毥毦毧毨毩毪毫毬毭毮毯毰毱毲毳毴毵毶毷毸毹毺毻毼毽毾毿 +氀氁氂氃氄氅氆氇氈氉氊氋氌氍氎氏氐民氒氓气氕氖気氘氙氚氛氜氝氞氟 +氠氡氢氣氤氥氦氧氨氩氪氫氬氭氮氯氰氱氲氳水氵氶氷永氹氺氻氼氽氾氿 +汀汁求汃汄汅汆汇汈汉汊汋汌汍汎汏汐汑汒汓汔汕汖汗汘汙汚汛汜汝汞江 +池污汢汣汤汥汦汧汨汩汪汫汬汭汮汯汰汱汲汳汴汵汶汷汸汹決汻汼汽汾汿 +沀沁沂沃沄沅沆沇沈沉沊沋沌沍沎沏沐沑沒沓沔沕沖沗沘沙沚沛沜沝沞沟 +沠没沢沣沤沥沦沧沨沩沪沫沬沭沮沯沰沱沲河沴沵沶沷沸油沺治沼沽沾沿 +泀況泂泃泄泅泆泇泈泉泊泋泌泍泎泏泐泑泒泓泔法泖泗泘泙泚泛泜泝泞泟 +泠泡波泣泤泥泦泧注泩泪泫泬泭泮泯泰泱泲泳泴泵泶泷泸泹泺泻泼泽泾泿 +洀洁洂洃洄洅洆洇洈洉洊洋洌洍洎洏洐洑洒洓洔洕洖洗洘洙洚洛洜洝洞洟 +洠洡洢洣洤津洦洧洨洩洪洫洬洭洮洯洰洱洲洳洴洵洶洷洸洹洺活洼洽派洿 +浀流浂浃浄浅浆浇浈浉浊测浌浍济浏浐浑浒浓浔浕浖浗浘浙浚浛浜浝浞浟 +浠浡浢浣浤浥浦浧浨浩浪浫浬浭浮浯浰浱浲浳浴浵浶海浸浹浺浻浼浽浾浿 +涀涁涂涃涄涅涆涇消涉涊涋涌涍涎涏涐涑涒涓涔涕涖涗涘涙涚涛涜涝涞涟 +涠涡涢涣涤涥润涧涨涩涪涫涬涭涮涯涰涱液涳涴涵涶涷涸涹涺涻涼涽涾涿 +淀淁淂淃淄淅淆淇淈淉淊淋淌淍淎淏淐淑淒淓淔淕淖淗淘淙淚淛淜淝淞淟 +淠淡淢淣淤淥淦淧淨淩淪淫淬淭淮淯淰深淲淳淴淵淶混淸淹淺添淼淽淾淿 +渀渁渂渃渄清渆渇済渉渊渋渌渍渎渏渐渑渒渓渔渕渖渗渘渙渚減渜渝渞渟 +渠渡渢渣渤渥渦渧渨温渪渫測渭渮港渰渱渲渳渴渵渶渷游渹渺渻渼渽渾渿 +湀湁湂湃湄湅湆湇湈湉湊湋湌湍湎湏湐湑湒湓湔湕湖湗湘湙湚湛湜湝湞湟 +湠湡湢湣湤湥湦湧湨湩湪湫湬湭湮湯湰湱湲湳湴湵湶湷湸湹湺湻湼湽湾湿 +満溁溂溃溄溅溆溇溈溉溊溋溌溍溎溏源溑溒溓溔溕準溗溘溙溚溛溜溝溞溟 +溠溡溢溣溤溥溦溧溨溩溪溫溬溭溮溯溰溱溲溳溴溵溶溷溸溹溺溻溼溽溾溿 +滀滁滂滃滄滅滆滇滈滉滊滋滌滍滎滏滐滑滒滓滔滕滖滗滘滙滚滛滜滝滞滟 +滠满滢滣滤滥滦滧滨滩滪滫滬滭滮滯滰滱滲滳滴滵滶滷滸滹滺滻滼滽滾滿 +漀漁漂漃漄漅漆漇漈漉漊漋漌漍漎漏漐漑漒漓演漕漖漗漘漙漚漛漜漝漞漟 +漠漡漢漣漤漥漦漧漨漩漪漫漬漭漮漯漰漱漲漳漴漵漶漷漸漹漺漻漼漽漾漿 +潀潁潂潃潄潅潆潇潈潉潊潋潌潍潎潏潐潑潒潓潔潕潖潗潘潙潚潛潜潝潞潟 +潠潡潢潣潤潥潦潧潨潩潪潫潬潭潮潯潰潱潲潳潴潵潶潷潸潹潺潻潼潽潾潿 +澀澁澂澃澄澅澆澇澈澉澊澋澌澍澎澏澐澑澒澓澔澕澖澗澘澙澚澛澜澝澞澟 +澠澡澢澣澤澥澦澧澨澩澪澫澬澭澮澯澰澱澲澳澴澵澶澷澸澹澺澻澼澽澾澿 +激濁濂濃濄濅濆濇濈濉濊濋濌濍濎濏濐濑濒濓濔濕濖濗濘濙濚濛濜濝濞濟 +濠濡濢濣濤濥濦濧濨濩濪濫濬濭濮濯濰濱濲濳濴濵濶濷濸濹濺濻濼濽濾濿 +瀀瀁瀂瀃瀄瀅瀆瀇瀈瀉瀊瀋瀌瀍瀎瀏瀐瀑瀒瀓瀔瀕瀖瀗瀘瀙瀚瀛瀜瀝瀞瀟 +瀠瀡瀢瀣瀤瀥瀦瀧瀨瀩瀪瀫瀬瀭瀮瀯瀰瀱瀲瀳瀴瀵瀶瀷瀸瀹瀺瀻瀼瀽瀾瀿 +灀灁灂灃灄灅灆灇灈灉灊灋灌灍灎灏灐灑灒灓灔灕灖灗灘灙灚灛灜灝灞灟 +灠灡灢灣灤灥灦灧灨灩灪火灬灭灮灯灰灱灲灳灴灵灶灷灸灹灺灻灼災灾灿 +炀炁炂炃炄炅炆炇炈炉炊炋炌炍炎炏炐炑炒炓炔炕炖炗炘炙炚炛炜炝炞炟 +炠炡炢炣炤炥炦炧炨炩炪炫炬炭炮炯炰炱炲炳炴炵炶炷炸点為炻炼炽炾炿 +烀烁烂烃烄烅烆烇烈烉烊烋烌烍烎烏烐烑烒烓烔烕烖烗烘烙烚烛烜烝烞烟 +烠烡烢烣烤烥烦烧烨烩烪烫烬热烮烯烰烱烲烳烴烵烶烷烸烹烺烻烼烽烾烿 +焀焁焂焃焄焅焆焇焈焉焊焋焌焍焎焏焐焑焒焓焔焕焖焗焘焙焚焛焜焝焞焟 +焠無焢焣焤焥焦焧焨焩焪焫焬焭焮焯焰焱焲焳焴焵然焷焸焹焺焻焼焽焾焿 +煀煁煂煃煄煅煆煇煈煉煊煋煌煍煎煏煐煑煒煓煔煕煖煗煘煙煚煛煜煝煞煟 +煠煡煢煣煤煥煦照煨煩煪煫煬煭煮煯煰煱煲煳煴煵煶煷煸煹煺煻煼煽煾煿 +熀熁熂熃熄熅熆熇熈熉熊熋熌熍熎熏熐熑熒熓熔熕熖熗熘熙熚熛熜熝熞熟 +熠熡熢熣熤熥熦熧熨熩熪熫熬熭熮熯熰熱熲熳熴熵熶熷熸熹熺熻熼熽熾熿 +燀燁燂燃燄燅燆燇燈燉燊燋燌燍燎燏燐燑燒燓燔燕燖燗燘燙燚燛燜燝燞營 +燠燡燢燣燤燥燦燧燨燩燪燫燬燭燮燯燰燱燲燳燴燵燶燷燸燹燺燻燼燽燾燿 +爀爁爂爃爄爅爆爇爈爉爊爋爌爍爎爏爐爑爒爓爔爕爖爗爘爙爚爛爜爝爞爟 +爠爡爢爣爤爥爦爧爨爩爪爫爬爭爮爯爰爱爲爳爴爵父爷爸爹爺爻爼爽爾爿 +牀牁牂牃牄牅牆片版牉牊牋牌牍牎牏牐牑牒牓牔牕牖牗牘牙牚牛牜牝牞牟 +牠牡牢牣牤牥牦牧牨物牪牫牬牭牮牯牰牱牲牳牴牵牶牷牸特牺牻牼牽牾牿 +犀犁犂犃犄犅犆犇犈犉犊犋犌犍犎犏犐犑犒犓犔犕犖犗犘犙犚犛犜犝犞犟 +犠犡犢犣犤犥犦犧犨犩犪犫犬犭犮犯犰犱犲犳犴犵状犷犸犹犺犻犼犽犾犿 +狀狁狂狃狄狅狆狇狈狉狊狋狌狍狎狏狐狑狒狓狔狕狖狗狘狙狚狛狜狝狞狟 +狠狡狢狣狤狥狦狧狨狩狪狫独狭狮狯狰狱狲狳狴狵狶狷狸狹狺狻狼狽狾狿 +猀猁猂猃猄猅猆猇猈猉猊猋猌猍猎猏猐猑猒猓猔猕猖猗猘猙猚猛猜猝猞猟 +猠猡猢猣猤猥猦猧猨猩猪猫猬猭献猯猰猱猲猳猴猵猶猷猸猹猺猻猼猽猾猿 +獀獁獂獃獄獅獆獇獈獉獊獋獌獍獎獏獐獑獒獓獔獕獖獗獘獙獚獛獜獝獞獟 +獠獡獢獣獤獥獦獧獨獩獪獫獬獭獮獯獰獱獲獳獴獵獶獷獸獹獺獻獼獽獾獿 +玀玁玂玃玄玅玆率玈玉玊王玌玍玎玏玐玑玒玓玔玕玖玗玘玙玚玛玜玝玞玟 +玠玡玢玣玤玥玦玧玨玩玪玫玬玭玮环现玱玲玳玴玵玶玷玸玹玺玻玼玽玾玿 +珀珁珂珃珄珅珆珇珈珉珊珋珌珍珎珏珐珑珒珓珔珕珖珗珘珙珚珛珜珝珞珟 +珠珡珢珣珤珥珦珧珨珩珪珫珬班珮珯珰珱珲珳珴珵珶珷珸珹珺珻珼珽現珿 +琀琁琂球琄琅理琇琈琉琊琋琌琍琎琏琐琑琒琓琔琕琖琗琘琙琚琛琜琝琞琟 +琠琡琢琣琤琥琦琧琨琩琪琫琬琭琮琯琰琱琲琳琴琵琶琷琸琹琺琻琼琽琾琿 +瑀瑁瑂瑃瑄瑅瑆瑇瑈瑉瑊瑋瑌瑍瑎瑏瑐瑑瑒瑓瑔瑕瑖瑗瑘瑙瑚瑛瑜瑝瑞瑟 +瑠瑡瑢瑣瑤瑥瑦瑧瑨瑩瑪瑫瑬瑭瑮瑯瑰瑱瑲瑳瑴瑵瑶瑷瑸瑹瑺瑻瑼瑽瑾瑿 +璀璁璂璃璄璅璆璇璈璉璊璋璌璍璎璏璐璑璒璓璔璕璖璗璘璙璚璛璜璝璞璟 +璠璡璢璣璤璥璦璧璨璩璪璫璬璭璮璯環璱璲璳璴璵璶璷璸璹璺璻璼璽璾璿 +瓀瓁瓂瓃瓄瓅瓆瓇瓈瓉瓊瓋瓌瓍瓎瓏瓐瓑瓒瓓瓔瓕瓖瓗瓘瓙瓚瓛瓜瓝瓞瓟 +瓠瓡瓢瓣瓤瓥瓦瓧瓨瓩瓪瓫瓬瓭瓮瓯瓰瓱瓲瓳瓴瓵瓶瓷瓸瓹瓺瓻瓼瓽瓾瓿 +甀甁甂甃甄甅甆甇甈甉甊甋甌甍甎甏甐甑甒甓甔甕甖甗甘甙甚甛甜甝甞生 +甠甡產産甤甥甦甧用甩甪甫甬甭甮甯田由甲申甴电甶男甸甹町画甼甽甾甿 +畀畁畂畃畄畅畆畇畈畉畊畋界畍畎畏畐畑畒畓畔畕畖畗畘留畚畛畜畝畞畟 +畠畡畢畣畤略畦畧畨畩番畫畬畭畮畯異畱畲畳畴畵當畷畸畹畺畻畼畽畾畿 +疀疁疂疃疄疅疆疇疈疉疊疋疌疍疎疏疐疑疒疓疔疕疖疗疘疙疚疛疜疝疞疟 +疠疡疢疣疤疥疦疧疨疩疪疫疬疭疮疯疰疱疲疳疴疵疶疷疸疹疺疻疼疽疾疿 +痀痁痂痃痄病痆症痈痉痊痋痌痍痎痏痐痑痒痓痔痕痖痗痘痙痚痛痜痝痞痟 +痠痡痢痣痤痥痦痧痨痩痪痫痬痭痮痯痰痱痲痳痴痵痶痷痸痹痺痻痼痽痾痿 +瘀瘁瘂瘃瘄瘅瘆瘇瘈瘉瘊瘋瘌瘍瘎瘏瘐瘑瘒瘓瘔瘕瘖瘗瘘瘙瘚瘛瘜瘝瘞瘟 +瘠瘡瘢瘣瘤瘥瘦瘧瘨瘩瘪瘫瘬瘭瘮瘯瘰瘱瘲瘳瘴瘵瘶瘷瘸瘹瘺瘻瘼瘽瘾瘿 +癀癁療癃癄癅癆癇癈癉癊癋癌癍癎癏癐癑癒癓癔癕癖癗癘癙癚癛癜癝癞癟 +癠癡癢癣癤癥癦癧癨癩癪癫癬癭癮癯癰癱癲癳癴癵癶癷癸癹発登發白百癿 +皀皁皂皃的皅皆皇皈皉皊皋皌皍皎皏皐皑皒皓皔皕皖皗皘皙皚皛皜皝皞皟 +皠皡皢皣皤皥皦皧皨皩皪皫皬皭皮皯皰皱皲皳皴皵皶皷皸皹皺皻皼皽皾皿 +盀盁盂盃盄盅盆盇盈盉益盋盌盍盎盏盐监盒盓盔盕盖盗盘盙盚盛盜盝盞盟 +盠盡盢監盤盥盦盧盨盩盪盫盬盭目盯盰盱盲盳直盵盶盷相盹盺盻盼盽盾盿 +眀省眂眃眄眅眆眇眈眉眊看県眍眎眏眐眑眒眓眔眕眖眗眘眙眚眛眜眝眞真 +眠眡眢眣眤眥眦眧眨眩眪眫眬眭眮眯眰眱眲眳眴眵眶眷眸眹眺眻眼眽眾眿 +着睁睂睃睄睅睆睇睈睉睊睋睌睍睎睏睐睑睒睓睔睕睖睗睘睙睚睛睜睝睞睟 +睠睡睢督睤睥睦睧睨睩睪睫睬睭睮睯睰睱睲睳睴睵睶睷睸睹睺睻睼睽睾睿 +瞀瞁瞂瞃瞄瞅瞆瞇瞈瞉瞊瞋瞌瞍瞎瞏瞐瞑瞒瞓瞔瞕瞖瞗瞘瞙瞚瞛瞜瞝瞞瞟 +瞠瞡瞢瞣瞤瞥瞦瞧瞨瞩瞪瞫瞬瞭瞮瞯瞰瞱瞲瞳瞴瞵瞶瞷瞸瞹瞺瞻瞼瞽瞾瞿 +矀矁矂矃矄矅矆矇矈矉矊矋矌矍矎矏矐矑矒矓矔矕矖矗矘矙矚矛矜矝矞矟 +矠矡矢矣矤知矦矧矨矩矪矫矬短矮矯矰矱矲石矴矵矶矷矸矹矺矻矼矽矾矿 +砀码砂砃砄砅砆砇砈砉砊砋砌砍砎砏砐砑砒砓研砕砖砗砘砙砚砛砜砝砞砟 +砠砡砢砣砤砥砦砧砨砩砪砫砬砭砮砯砰砱砲砳破砵砶砷砸砹砺砻砼砽砾砿 +础硁硂硃硄硅硆硇硈硉硊硋硌硍硎硏硐硑硒硓硔硕硖硗硘硙硚硛硜硝硞硟 +硠硡硢硣硤硥硦硧硨硩硪硫硬硭确硯硰硱硲硳硴硵硶硷硸硹硺硻硼硽硾硿 +碀碁碂碃碄碅碆碇碈碉碊碋碌碍碎碏碐碑碒碓碔碕碖碗碘碙碚碛碜碝碞碟 +碠碡碢碣碤碥碦碧碨碩碪碫碬碭碮碯碰碱碲碳碴碵碶碷碸碹確碻碼碽碾碿 +磀磁磂磃磄磅磆磇磈磉磊磋磌磍磎磏磐磑磒磓磔磕磖磗磘磙磚磛磜磝磞磟 +磠磡磢磣磤磥磦磧磨磩磪磫磬磭磮磯磰磱磲磳磴磵磶磷磸磹磺磻磼磽磾磿 +礀礁礂礃礄礅礆礇礈礉礊礋礌礍礎礏礐礑礒礓礔礕礖礗礘礙礚礛礜礝礞礟 +礠礡礢礣礤礥礦礧礨礩礪礫礬礭礮礯礰礱礲礳礴礵礶礷礸礹示礻礼礽社礿 +祀祁祂祃祄祅祆祇祈祉祊祋祌祍祎祏祐祑祒祓祔祕祖祗祘祙祚祛祜祝神祟 +祠祡祢祣祤祥祦祧票祩祪祫祬祭祮祯祰祱祲祳祴祵祶祷祸祹祺祻祼祽祾祿 +禀禁禂禃禄禅禆禇禈禉禊禋禌禍禎福禐禑禒禓禔禕禖禗禘禙禚禛禜禝禞禟 +禠禡禢禣禤禥禦禧禨禩禪禫禬禭禮禯禰禱禲禳禴禵禶禷禸禹禺离禼禽禾禿 +秀私秂秃秄秅秆秇秈秉秊秋秌种秎秏秐科秒秓秔秕秖秗秘秙秚秛秜秝秞租 +秠秡秢秣秤秥秦秧秨秩秪秫秬秭秮积称秱秲秳秴秵秶秷秸秹秺移秼秽秾秿 +稀稁稂稃稄稅稆稇稈稉稊程稌稍税稏稐稑稒稓稔稕稖稗稘稙稚稛稜稝稞稟 +稠稡稢稣稤稥稦稧稨稩稪稫稬稭種稯稰稱稲稳稴稵稶稷稸稹稺稻稼稽稾稿 +穀穁穂穃穄穅穆穇穈穉穊穋穌積穎穏穐穑穒穓穔穕穖穗穘穙穚穛穜穝穞穟 +穠穡穢穣穤穥穦穧穨穩穪穫穬穭穮穯穰穱穲穳穴穵究穷穸穹空穻穼穽穾穿 +窀突窂窃窄窅窆窇窈窉窊窋窌窍窎窏窐窑窒窓窔窕窖窗窘窙窚窛窜窝窞窟 +窠窡窢窣窤窥窦窧窨窩窪窫窬窭窮窯窰窱窲窳窴窵窶窷窸窹窺窻窼窽窾窿 +竀竁竂竃竄竅竆竇竈竉竊立竌竍竎竏竐竑竒竓竔竕竖竗竘站竚竛竜竝竞竟 +章竡竢竣竤童竦竧竨竩竪竫竬竭竮端竰竱竲竳竴竵競竷竸竹竺竻竼竽竾竿 +笀笁笂笃笄笅笆笇笈笉笊笋笌笍笎笏笐笑笒笓笔笕笖笗笘笙笚笛笜笝笞笟 +笠笡笢笣笤笥符笧笨笩笪笫第笭笮笯笰笱笲笳笴笵笶笷笸笹笺笻笼笽笾笿 +筀筁筂筃筄筅筆筇筈等筊筋筌筍筎筏筐筑筒筓答筕策筗筘筙筚筛筜筝筞筟 +筠筡筢筣筤筥筦筧筨筩筪筫筬筭筮筯筰筱筲筳筴筵筶筷筸筹筺筻筼筽签筿 +简箁箂箃箄箅箆箇箈箉箊箋箌箍箎箏箐箑箒箓箔箕箖算箘箙箚箛箜箝箞箟 +箠管箢箣箤箥箦箧箨箩箪箫箬箭箮箯箰箱箲箳箴箵箶箷箸箹箺箻箼箽箾箿 +節篁篂篃範篅篆篇篈築篊篋篌篍篎篏篐篑篒篓篔篕篖篗篘篙篚篛篜篝篞篟 +篠篡篢篣篤篥篦篧篨篩篪篫篬篭篮篯篰篱篲篳篴篵篶篷篸篹篺篻篼篽篾篿 +簀簁簂簃簄簅簆簇簈簉簊簋簌簍簎簏簐簑簒簓簔簕簖簗簘簙簚簛簜簝簞簟 +簠簡簢簣簤簥簦簧簨簩簪簫簬簭簮簯簰簱簲簳簴簵簶簷簸簹簺簻簼簽簾簿 +籀籁籂籃籄籅籆籇籈籉籊籋籌籍籎籏籐籑籒籓籔籕籖籗籘籙籚籛籜籝籞籟 +籠籡籢籣籤籥籦籧籨籩籪籫籬籭籮籯籰籱籲米籴籵籶籷籸籹籺类籼籽籾籿 +粀粁粂粃粄粅粆粇粈粉粊粋粌粍粎粏粐粑粒粓粔粕粖粗粘粙粚粛粜粝粞粟 +粠粡粢粣粤粥粦粧粨粩粪粫粬粭粮粯粰粱粲粳粴粵粶粷粸粹粺粻粼粽精粿 +糀糁糂糃糄糅糆糇糈糉糊糋糌糍糎糏糐糑糒糓糔糕糖糗糘糙糚糛糜糝糞糟 +糠糡糢糣糤糥糦糧糨糩糪糫糬糭糮糯糰糱糲糳糴糵糶糷糸糹糺系糼糽糾糿 +紀紁紂紃約紅紆紇紈紉紊紋紌納紎紏紐紑紒紓純紕紖紗紘紙級紛紜紝紞紟 +素紡索紣紤紥紦紧紨紩紪紫紬紭紮累細紱紲紳紴紵紶紷紸紹紺紻紼紽紾紿 +絀絁終絃組絅絆絇絈絉絊絋経絍絎絏結絑絒絓絔絕絖絗絘絙絚絛絜絝絞絟 +絠絡絢絣絤絥給絧絨絩絪絫絬絭絮絯絰統絲絳絴絵絶絷絸絹絺絻絼絽絾絿 +綀綁綂綃綄綅綆綇綈綉綊綋綌綍綎綏綐綑綒經綔綕綖綗綘継続綛綜綝綞綟 +綠綡綢綣綤綥綦綧綨綩綪綫綬維綮綯綰綱網綳綴綵綶綷綸綹綺綻綼綽綾綿 +緀緁緂緃緄緅緆緇緈緉緊緋緌緍緎総緐緑緒緓緔緕緖緗緘緙線緛緜緝緞緟 +締緡緢緣緤緥緦緧編緩緪緫緬緭緮緯緰緱緲緳練緵緶緷緸緹緺緻緼緽緾緿 +縀縁縂縃縄縅縆縇縈縉縊縋縌縍縎縏縐縑縒縓縔縕縖縗縘縙縚縛縜縝縞縟 +縠縡縢縣縤縥縦縧縨縩縪縫縬縭縮縯縰縱縲縳縴縵縶縷縸縹縺縻縼總績縿 +繀繁繂繃繄繅繆繇繈繉繊繋繌繍繎繏繐繑繒繓織繕繖繗繘繙繚繛繜繝繞繟 +繠繡繢繣繤繥繦繧繨繩繪繫繬繭繮繯繰繱繲繳繴繵繶繷繸繹繺繻繼繽繾繿 +纀纁纂纃纄纅纆纇纈纉纊纋續纍纎纏纐纑纒纓纔纕纖纗纘纙纚纛纜纝纞纟 +纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线 +绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绖绗绘给绚绛络绝绞统 +绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿 +缀缁缂缃缄缅缆缇缈缉缊缋缌缍缎缏缐缑缒缓缔缕编缗缘缙缚缛缜缝缞缟 +缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵缶缷缸缹缺缻缼缽缾缿 +罀罁罂罃罄罅罆罇罈罉罊罋罌罍罎罏罐网罒罓罔罕罖罗罘罙罚罛罜罝罞罟 +罠罡罢罣罤罥罦罧罨罩罪罫罬罭置罯罰罱署罳罴罵罶罷罸罹罺罻罼罽罾罿 +羀羁羂羃羄羅羆羇羈羉羊羋羌羍美羏羐羑羒羓羔羕羖羗羘羙羚羛羜羝羞羟 +羠羡羢羣群羥羦羧羨義羪羫羬羭羮羯羰羱羲羳羴羵羶羷羸羹羺羻羼羽羾羿 +翀翁翂翃翄翅翆翇翈翉翊翋翌翍翎翏翐翑習翓翔翕翖翗翘翙翚翛翜翝翞翟 +翠翡翢翣翤翥翦翧翨翩翪翫翬翭翮翯翰翱翲翳翴翵翶翷翸翹翺翻翼翽翾翿 +耀老耂考耄者耆耇耈耉耊耋而耍耎耏耐耑耒耓耔耕耖耗耘耙耚耛耜耝耞耟 +耠耡耢耣耤耥耦耧耨耩耪耫耬耭耮耯耰耱耲耳耴耵耶耷耸耹耺耻耼耽耾耿 +聀聁聂聃聄聅聆聇聈聉聊聋职聍聎聏聐聑聒聓联聕聖聗聘聙聚聛聜聝聞聟 +聠聡聢聣聤聥聦聧聨聩聪聫聬聭聮聯聰聱聲聳聴聵聶職聸聹聺聻聼聽聾聿 +肀肁肂肃肄肅肆肇肈肉肊肋肌肍肎肏肐肑肒肓肔肕肖肗肘肙肚肛肜肝肞肟 +肠股肢肣肤肥肦肧肨肩肪肫肬肭肮肯肰肱育肳肴肵肶肷肸肹肺肻肼肽肾肿 +胀胁胂胃胄胅胆胇胈胉胊胋背胍胎胏胐胑胒胓胔胕胖胗胘胙胚胛胜胝胞胟 +胠胡胢胣胤胥胦胧胨胩胪胫胬胭胮胯胰胱胲胳胴胵胶胷胸胹胺胻胼能胾胿 +脀脁脂脃脄脅脆脇脈脉脊脋脌脍脎脏脐脑脒脓脔脕脖脗脘脙脚脛脜脝脞脟 +脠脡脢脣脤脥脦脧脨脩脪脫脬脭脮脯脰脱脲脳脴脵脶脷脸脹脺脻脼脽脾脿 +腀腁腂腃腄腅腆腇腈腉腊腋腌腍腎腏腐腑腒腓腔腕腖腗腘腙腚腛腜腝腞腟 +腠腡腢腣腤腥腦腧腨腩腪腫腬腭腮腯腰腱腲腳腴腵腶腷腸腹腺腻腼腽腾腿 +膀膁膂膃膄膅膆膇膈膉膊膋膌膍膎膏膐膑膒膓膔膕膖膗膘膙膚膛膜膝膞膟 +膠膡膢膣膤膥膦膧膨膩膪膫膬膭膮膯膰膱膲膳膴膵膶膷膸膹膺膻膼膽膾膿 +臀臁臂臃臄臅臆臇臈臉臊臋臌臍臎臏臐臑臒臓臔臕臖臗臘臙臚臛臜臝臞臟 +臠臡臢臣臤臥臦臧臨臩自臫臬臭臮臯臰臱臲至致臵臶臷臸臹臺臻臼臽臾臿 +舀舁舂舃舄舅舆與興舉舊舋舌舍舎舏舐舑舒舓舔舕舖舗舘舙舚舛舜舝舞舟 +舠舡舢舣舤舥舦舧舨舩航舫般舭舮舯舰舱舲舳舴舵舶舷舸船舺舻舼舽舾舿 +艀艁艂艃艄艅艆艇艈艉艊艋艌艍艎艏艐艑艒艓艔艕艖艗艘艙艚艛艜艝艞艟 +艠艡艢艣艤艥艦艧艨艩艪艫艬艭艮良艰艱色艳艴艵艶艷艸艹艺艻艼艽艾艿 +芀芁节芃芄芅芆芇芈芉芊芋芌芍芎芏芐芑芒芓芔芕芖芗芘芙芚芛芜芝芞芟 +芠芡芢芣芤芥芦芧芨芩芪芫芬芭芮芯芰花芲芳芴芵芶芷芸芹芺芻芼芽芾芿 +苀苁苂苃苄苅苆苇苈苉苊苋苌苍苎苏苐苑苒苓苔苕苖苗苘苙苚苛苜苝苞苟 +苠苡苢苣苤若苦苧苨苩苪苫苬苭苮苯苰英苲苳苴苵苶苷苸苹苺苻苼苽苾苿 +茀茁茂范茄茅茆茇茈茉茊茋茌茍茎茏茐茑茒茓茔茕茖茗茘茙茚茛茜茝茞茟 +茠茡茢茣茤茥茦茧茨茩茪茫茬茭茮茯茰茱茲茳茴茵茶茷茸茹茺茻茼茽茾茿 +荀荁荂荃荄荅荆荇荈草荊荋荌荍荎荏荐荑荒荓荔荕荖荗荘荙荚荛荜荝荞荟 +荠荡荢荣荤荥荦荧荨荩荪荫荬荭荮药荰荱荲荳荴荵荶荷荸荹荺荻荼荽荾荿 +莀莁莂莃莄莅莆莇莈莉莊莋莌莍莎莏莐莑莒莓莔莕莖莗莘莙莚莛莜莝莞莟 +莠莡莢莣莤莥莦莧莨莩莪莫莬莭莮莯莰莱莲莳莴莵莶获莸莹莺莻莼莽莾莿 +菀菁菂菃菄菅菆菇菈菉菊菋菌菍菎菏菐菑菒菓菔菕菖菗菘菙菚菛菜菝菞菟 +菠菡菢菣菤菥菦菧菨菩菪菫菬菭菮華菰菱菲菳菴菵菶菷菸菹菺菻菼菽菾菿 +萀萁萂萃萄萅萆萇萈萉萊萋萌萍萎萏萐萑萒萓萔萕萖萗萘萙萚萛萜萝萞萟 +萠萡萢萣萤营萦萧萨萩萪萫萬萭萮萯萰萱萲萳萴萵萶萷萸萹萺萻萼落萾萿 +葀葁葂葃葄葅葆葇葈葉葊葋葌葍葎葏葐葑葒葓葔葕葖著葘葙葚葛葜葝葞葟 +葠葡葢董葤葥葦葧葨葩葪葫葬葭葮葯葰葱葲葳葴葵葶葷葸葹葺葻葼葽葾葿 +蒀蒁蒂蒃蒄蒅蒆蒇蒈蒉蒊蒋蒌蒍蒎蒏蒐蒑蒒蒓蒔蒕蒖蒗蒘蒙蒚蒛蒜蒝蒞蒟 +蒠蒡蒢蒣蒤蒥蒦蒧蒨蒩蒪蒫蒬蒭蒮蒯蒰蒱蒲蒳蒴蒵蒶蒷蒸蒹蒺蒻蒼蒽蒾蒿 +蓀蓁蓂蓃蓄蓅蓆蓇蓈蓉蓊蓋蓌蓍蓎蓏蓐蓑蓒蓓蓔蓕蓖蓗蓘蓙蓚蓛蓜蓝蓞蓟 +蓠蓡蓢蓣蓤蓥蓦蓧蓨蓩蓪蓫蓬蓭蓮蓯蓰蓱蓲蓳蓴蓵蓶蓷蓸蓹蓺蓻蓼蓽蓾蓿 +蔀蔁蔂蔃蔄蔅蔆蔇蔈蔉蔊蔋蔌蔍蔎蔏蔐蔑蔒蔓蔔蔕蔖蔗蔘蔙蔚蔛蔜蔝蔞蔟 +蔠蔡蔢蔣蔤蔥蔦蔧蔨蔩蔪蔫蔬蔭蔮蔯蔰蔱蔲蔳蔴蔵蔶蔷蔸蔹蔺蔻蔼蔽蔾蔿 +蕀蕁蕂蕃蕄蕅蕆蕇蕈蕉蕊蕋蕌蕍蕎蕏蕐蕑蕒蕓蕔蕕蕖蕗蕘蕙蕚蕛蕜蕝蕞蕟 +蕠蕡蕢蕣蕤蕥蕦蕧蕨蕩蕪蕫蕬蕭蕮蕯蕰蕱蕲蕳蕴蕵蕶蕷蕸蕹蕺蕻蕼蕽蕾蕿 +薀薁薂薃薄薅薆薇薈薉薊薋薌薍薎薏薐薑薒薓薔薕薖薗薘薙薚薛薜薝薞薟 +薠薡薢薣薤薥薦薧薨薩薪薫薬薭薮薯薰薱薲薳薴薵薶薷薸薹薺薻薼薽薾薿 +藀藁藂藃藄藅藆藇藈藉藊藋藌藍藎藏藐藑藒藓藔藕藖藗藘藙藚藛藜藝藞藟 +藠藡藢藣藤藥藦藧藨藩藪藫藬藭藮藯藰藱藲藳藴藵藶藷藸藹藺藻藼藽藾藿 +蘀蘁蘂蘃蘄蘅蘆蘇蘈蘉蘊蘋蘌蘍蘎蘏蘐蘑蘒蘓蘔蘕蘖蘗蘘蘙蘚蘛蘜蘝蘞蘟 +蘠蘡蘢蘣蘤蘥蘦蘧蘨蘩蘪蘫蘬蘭蘮蘯蘰蘱蘲蘳蘴蘵蘶蘷蘸蘹蘺蘻蘼蘽蘾蘿 +虀虁虂虃虄虅虆虇虈虉虊虋虌虍虎虏虐虑虒虓虔處虖虗虘虙虚虛虜虝虞號 +虠虡虢虣虤虥虦虧虨虩虪虫虬虭虮虯虰虱虲虳虴虵虶虷虸虹虺虻虼虽虾虿 +蚀蚁蚂蚃蚄蚅蚆蚇蚈蚉蚊蚋蚌蚍蚎蚏蚐蚑蚒蚓蚔蚕蚖蚗蚘蚙蚚蚛蚜蚝蚞蚟 +蚠蚡蚢蚣蚤蚥蚦蚧蚨蚩蚪蚫蚬蚭蚮蚯蚰蚱蚲蚳蚴蚵蚶蚷蚸蚹蚺蚻蚼蚽蚾蚿 +蛀蛁蛂蛃蛄蛅蛆蛇蛈蛉蛊蛋蛌蛍蛎蛏蛐蛑蛒蛓蛔蛕蛖蛗蛘蛙蛚蛛蛜蛝蛞蛟 +蛠蛡蛢蛣蛤蛥蛦蛧蛨蛩蛪蛫蛬蛭蛮蛯蛰蛱蛲蛳蛴蛵蛶蛷蛸蛹蛺蛻蛼蛽蛾蛿 +蜀蜁蜂蜃蜄蜅蜆蜇蜈蜉蜊蜋蜌蜍蜎蜏蜐蜑蜒蜓蜔蜕蜖蜗蜘蜙蜚蜛蜜蜝蜞蜟 +蜠蜡蜢蜣蜤蜥蜦蜧蜨蜩蜪蜫蜬蜭蜮蜯蜰蜱蜲蜳蜴蜵蜶蜷蜸蜹蜺蜻蜼蜽蜾蜿 +蝀蝁蝂蝃蝄蝅蝆蝇蝈蝉蝊蝋蝌蝍蝎蝏蝐蝑蝒蝓蝔蝕蝖蝗蝘蝙蝚蝛蝜蝝蝞蝟 +蝠蝡蝢蝣蝤蝥蝦蝧蝨蝩蝪蝫蝬蝭蝮蝯蝰蝱蝲蝳蝴蝵蝶蝷蝸蝹蝺蝻蝼蝽蝾蝿 +螀螁螂螃螄螅螆螇螈螉螊螋螌融螎螏螐螑螒螓螔螕螖螗螘螙螚螛螜螝螞螟 +螠螡螢螣螤螥螦螧螨螩螪螫螬螭螮螯螰螱螲螳螴螵螶螷螸螹螺螻螼螽螾螿 +蟀蟁蟂蟃蟄蟅蟆蟇蟈蟉蟊蟋蟌蟍蟎蟏蟐蟑蟒蟓蟔蟕蟖蟗蟘蟙蟚蟛蟜蟝蟞蟟 +蟠蟡蟢蟣蟤蟥蟦蟧蟨蟩蟪蟫蟬蟭蟮蟯蟰蟱蟲蟳蟴蟵蟶蟷蟸蟹蟺蟻蟼蟽蟾蟿 +蠀蠁蠂蠃蠄蠅蠆蠇蠈蠉蠊蠋蠌蠍蠎蠏蠐蠑蠒蠓蠔蠕蠖蠗蠘蠙蠚蠛蠜蠝蠞蠟 +蠠蠡蠢蠣蠤蠥蠦蠧蠨蠩蠪蠫蠬蠭蠮蠯蠰蠱蠲蠳蠴蠵蠶蠷蠸蠹蠺蠻蠼蠽蠾蠿 +血衁衂衃衄衅衆衇衈衉衊衋行衍衎衏衐衑衒術衔衕衖街衘衙衚衛衜衝衞衟 +衠衡衢衣衤补衦衧表衩衪衫衬衭衮衯衰衱衲衳衴衵衶衷衸衹衺衻衼衽衾衿 +袀袁袂袃袄袅袆袇袈袉袊袋袌袍袎袏袐袑袒袓袔袕袖袗袘袙袚袛袜袝袞袟 +袠袡袢袣袤袥袦袧袨袩袪被袬袭袮袯袰袱袲袳袴袵袶袷袸袹袺袻袼袽袾袿 +裀裁裂裃裄装裆裇裈裉裊裋裌裍裎裏裐裑裒裓裔裕裖裗裘裙裚裛補裝裞裟 +裠裡裢裣裤裥裦裧裨裩裪裫裬裭裮裯裰裱裲裳裴裵裶裷裸裹裺裻裼製裾裿 +褀褁褂褃褄褅褆複褈褉褊褋褌褍褎褏褐褑褒褓褔褕褖褗褘褙褚褛褜褝褞褟 +褠褡褢褣褤褥褦褧褨褩褪褫褬褭褮褯褰褱褲褳褴褵褶褷褸褹褺褻褼褽褾褿 +襀襁襂襃襄襅襆襇襈襉襊襋襌襍襎襏襐襑襒襓襔襕襖襗襘襙襚襛襜襝襞襟 +襠襡襢襣襤襥襦襧襨襩襪襫襬襭襮襯襰襱襲襳襴襵襶襷襸襹襺襻襼襽襾西 +覀要覂覃覄覅覆覇覈覉覊見覌覍覎規覐覑覒覓覔覕視覗覘覙覚覛覜覝覞覟 +覠覡覢覣覤覥覦覧覨覩親覫覬覭覮覯覰覱覲観覴覵覶覷覸覹覺覻覼覽覾覿 +觀见观觃规觅视觇览觉觊觋觌觍觎觏觐觑角觓觔觕觖觗觘觙觚觛觜觝觞觟 +觠觡觢解觤觥触觧觨觩觪觫觬觭觮觯觰觱觲觳觴觵觶觷觸觹觺觻觼觽觾觿 +言訁訂訃訄訅訆訇計訉訊訋訌訍討訏訐訑訒訓訔訕訖託記訙訚訛訜訝訞訟 +訠訡訢訣訤訥訦訧訨訩訪訫訬設訮訯訰許訲訳訴訵訶訷訸訹診註証訽訾訿 +詀詁詂詃詄詅詆詇詈詉詊詋詌詍詎詏詐詑詒詓詔評詖詗詘詙詚詛詜詝詞詟 +詠詡詢詣詤詥試詧詨詩詪詫詬詭詮詯詰話該詳詴詵詶詷詸詹詺詻詼詽詾詿 +誀誁誂誃誄誅誆誇誈誉誊誋誌認誎誏誐誑誒誓誔誕誖誗誘誙誚誛誜誝語誟 +誠誡誢誣誤誥誦誧誨誩說誫説読誮誯誰誱課誳誴誵誶誷誸誹誺誻誼誽誾調 +諀諁諂諃諄諅諆談諈諉諊請諌諍諎諏諐諑諒諓諔諕論諗諘諙諚諛諜諝諞諟 +諠諡諢諣諤諥諦諧諨諩諪諫諬諭諮諯諰諱諲諳諴諵諶諷諸諹諺諻諼諽諾諿 +謀謁謂謃謄謅謆謇謈謉謊謋謌謍謎謏謐謑謒謓謔謕謖謗謘謙謚講謜謝謞謟 +謠謡謢謣謤謥謦謧謨謩謪謫謬謭謮謯謰謱謲謳謴謵謶謷謸謹謺謻謼謽謾謿 +譀譁譂譃譄譅譆譇譈證譊譋譌譍譎譏譐譑譒譓譔譕譖譗識譙譚譛譜譝譞譟 +譠譡譢譣譤譥警譧譨譩譪譫譬譭譮譯議譱譲譳譴譵譶護譸譹譺譻譼譽譾譿 +讀讁讂讃讄讅讆讇讈讉變讋讌讍讎讏讐讑讒讓讔讕讖讗讘讙讚讛讜讝讞讟 +讠计订讣认讥讦讧讨让讪讫讬训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访 +诀证诂诃评诅识诇诈诉诊诋诌词诎诏诐译诒诓诔试诖诗诘诙诚诛诜话诞诟 +诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿 +谀谁谂调谄谅谆谇谈谉谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谞谟 +谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷谸谹谺谻谼谽谾谿 +豀豁豂豃豄豅豆豇豈豉豊豋豌豍豎豏豐豑豒豓豔豕豖豗豘豙豚豛豜豝豞豟 +豠象豢豣豤豥豦豧豨豩豪豫豬豭豮豯豰豱豲豳豴豵豶豷豸豹豺豻豼豽豾豿 +貀貁貂貃貄貅貆貇貈貉貊貋貌貍貎貏貐貑貒貓貔貕貖貗貘貙貚貛貜貝貞貟 +負財貢貣貤貥貦貧貨販貪貫責貭貮貯貰貱貲貳貴貵貶買貸貹貺費貼貽貾貿 +賀賁賂賃賄賅賆資賈賉賊賋賌賍賎賏賐賑賒賓賔賕賖賗賘賙賚賛賜賝賞賟 +賠賡賢賣賤賥賦賧賨賩質賫賬賭賮賯賰賱賲賳賴賵賶賷賸賹賺賻購賽賾賿 +贀贁贂贃贄贅贆贇贈贉贊贋贌贍贎贏贐贑贒贓贔贕贖贗贘贙贚贛贜贝贞负 +贠贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿 +赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赑赒赓赔赕赖赗赘赙赚赛赜赝赞赟 +赠赡赢赣赤赥赦赧赨赩赪赫赬赭赮赯走赱赲赳赴赵赶起赸赹赺赻赼赽赾赿 +趀趁趂趃趄超趆趇趈趉越趋趌趍趎趏趐趑趒趓趔趕趖趗趘趙趚趛趜趝趞趟 +趠趡趢趣趤趥趦趧趨趩趪趫趬趭趮趯趰趱趲足趴趵趶趷趸趹趺趻趼趽趾趿 +跀跁跂跃跄跅跆跇跈跉跊跋跌跍跎跏跐跑跒跓跔跕跖跗跘跙跚跛跜距跞跟 +跠跡跢跣跤跥跦跧跨跩跪跫跬跭跮路跰跱跲跳跴践跶跷跸跹跺跻跼跽跾跿 +踀踁踂踃踄踅踆踇踈踉踊踋踌踍踎踏踐踑踒踓踔踕踖踗踘踙踚踛踜踝踞踟 +踠踡踢踣踤踥踦踧踨踩踪踫踬踭踮踯踰踱踲踳踴踵踶踷踸踹踺踻踼踽踾踿 +蹀蹁蹂蹃蹄蹅蹆蹇蹈蹉蹊蹋蹌蹍蹎蹏蹐蹑蹒蹓蹔蹕蹖蹗蹘蹙蹚蹛蹜蹝蹞蹟 +蹠蹡蹢蹣蹤蹥蹦蹧蹨蹩蹪蹫蹬蹭蹮蹯蹰蹱蹲蹳蹴蹵蹶蹷蹸蹹蹺蹻蹼蹽蹾蹿 +躀躁躂躃躄躅躆躇躈躉躊躋躌躍躎躏躐躑躒躓躔躕躖躗躘躙躚躛躜躝躞躟 +躠躡躢躣躤躥躦躧躨躩躪身躬躭躮躯躰躱躲躳躴躵躶躷躸躹躺躻躼躽躾躿 +軀軁軂軃軄軅軆軇軈軉車軋軌軍軎軏軐軑軒軓軔軕軖軗軘軙軚軛軜軝軞軟 +軠軡転軣軤軥軦軧軨軩軪軫軬軭軮軯軰軱軲軳軴軵軶軷軸軹軺軻軼軽軾軿 +輀輁輂較輄輅輆輇輈載輊輋輌輍輎輏輐輑輒輓輔輕輖輗輘輙輚輛輜輝輞輟 +輠輡輢輣輤輥輦輧輨輩輪輫輬輭輮輯輰輱輲輳輴輵輶輷輸輹輺輻輼輽輾輿 +轀轁轂轃轄轅轆轇轈轉轊轋轌轍轎轏轐轑轒轓轔轕轖轗轘轙轚轛轜轝轞轟 +轠轡轢轣轤轥车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿 +辀辁辂较辄辅辆辇辈辉辊辋辌辍辎辏辐辑辒输辔辕辖辗辘辙辚辛辜辝辞辟 +辠辡辢辣辤辥辦辧辨辩辪辫辬辭辮辯辰辱農辳辴辵辶辷辸边辺辻込辽达辿 +迀迁迂迃迄迅迆过迈迉迊迋迌迍迎迏运近迒迓返迕迖迗还这迚进远违连迟 +迠迡迢迣迤迥迦迧迨迩迪迫迬迭迮迯述迱迲迳迴迵迶迷迸迹迺迻迼追迾迿 +退送适逃逄逅逆逇逈选逊逋逌逍逎透逐逑递逓途逕逖逗逘這通逛逜逝逞速 +造逡逢連逤逥逦逧逨逩逪逫逬逭逮逯逰週進逳逴逵逶逷逸逹逺逻逼逽逾逿 +遀遁遂遃遄遅遆遇遈遉遊運遌遍過遏遐遑遒道達違遖遗遘遙遚遛遜遝遞遟 +遠遡遢遣遤遥遦遧遨適遪遫遬遭遮遯遰遱遲遳遴遵遶遷選遹遺遻遼遽遾避 +邀邁邂邃還邅邆邇邈邉邊邋邌邍邎邏邐邑邒邓邔邕邖邗邘邙邚邛邜邝邞邟 +邠邡邢那邤邥邦邧邨邩邪邫邬邭邮邯邰邱邲邳邴邵邶邷邸邹邺邻邼邽邾邿 +郀郁郂郃郄郅郆郇郈郉郊郋郌郍郎郏郐郑郒郓郔郕郖郗郘郙郚郛郜郝郞郟 +郠郡郢郣郤郥郦郧部郩郪郫郬郭郮郯郰郱郲郳郴郵郶郷郸郹郺郻郼都郾郿 +鄀鄁鄂鄃鄄鄅鄆鄇鄈鄉鄊鄋鄌鄍鄎鄏鄐鄑鄒鄓鄔鄕鄖鄗鄘鄙鄚鄛鄜鄝鄞鄟 +鄠鄡鄢鄣鄤鄥鄦鄧鄨鄩鄪鄫鄬鄭鄮鄯鄰鄱鄲鄳鄴鄵鄶鄷鄸鄹鄺鄻鄼鄽鄾鄿 +酀酁酂酃酄酅酆酇酈酉酊酋酌配酎酏酐酑酒酓酔酕酖酗酘酙酚酛酜酝酞酟 +酠酡酢酣酤酥酦酧酨酩酪酫酬酭酮酯酰酱酲酳酴酵酶酷酸酹酺酻酼酽酾酿 +醀醁醂醃醄醅醆醇醈醉醊醋醌醍醎醏醐醑醒醓醔醕醖醗醘醙醚醛醜醝醞醟 +醠醡醢醣醤醥醦醧醨醩醪醫醬醭醮醯醰醱醲醳醴醵醶醷醸醹醺醻醼醽醾醿 +釀釁釂釃釄釅釆采釈釉释釋里重野量釐金釒釓釔釕釖釗釘釙釚釛釜針釞釟 +釠釡釢釣釤釥釦釧釨釩釪釫釬釭釮釯釰釱釲釳釴釵釶釷釸釹釺釻釼釽釾釿 +鈀鈁鈂鈃鈄鈅鈆鈇鈈鈉鈊鈋鈌鈍鈎鈏鈐鈑鈒鈓鈔鈕鈖鈗鈘鈙鈚鈛鈜鈝鈞鈟 +鈠鈡鈢鈣鈤鈥鈦鈧鈨鈩鈪鈫鈬鈭鈮鈯鈰鈱鈲鈳鈴鈵鈶鈷鈸鈹鈺鈻鈼鈽鈾鈿 +鉀鉁鉂鉃鉄鉅鉆鉇鉈鉉鉊鉋鉌鉍鉎鉏鉐鉑鉒鉓鉔鉕鉖鉗鉘鉙鉚鉛鉜鉝鉞鉟 +鉠鉡鉢鉣鉤鉥鉦鉧鉨鉩鉪鉫鉬鉭鉮鉯鉰鉱鉲鉳鉴鉵鉶鉷鉸鉹鉺鉻鉼鉽鉾鉿 +銀銁銂銃銄銅銆銇銈銉銊銋銌銍銎銏銐銑銒銓銔銕銖銗銘銙銚銛銜銝銞銟 +銠銡銢銣銤銥銦銧銨銩銪銫銬銭銮銯銰銱銲銳銴銵銶銷銸銹銺銻銼銽銾銿 +鋀鋁鋂鋃鋄鋅鋆鋇鋈鋉鋊鋋鋌鋍鋎鋏鋐鋑鋒鋓鋔鋕鋖鋗鋘鋙鋚鋛鋜鋝鋞鋟 +鋠鋡鋢鋣鋤鋥鋦鋧鋨鋩鋪鋫鋬鋭鋮鋯鋰鋱鋲鋳鋴鋵鋶鋷鋸鋹鋺鋻鋼鋽鋾鋿 +錀錁錂錃錄錅錆錇錈錉錊錋錌錍錎錏錐錑錒錓錔錕錖錗錘錙錚錛錜錝錞錟 +錠錡錢錣錤錥錦錧錨錩錪錫錬錭錮錯錰錱録錳錴錵錶錷錸錹錺錻錼錽錾錿 +鍀鍁鍂鍃鍄鍅鍆鍇鍈鍉鍊鍋鍌鍍鍎鍏鍐鍑鍒鍓鍔鍕鍖鍗鍘鍙鍚鍛鍜鍝鍞鍟 +鍠鍡鍢鍣鍤鍥鍦鍧鍨鍩鍪鍫鍬鍭鍮鍯鍰鍱鍲鍳鍴鍵鍶鍷鍸鍹鍺鍻鍼鍽鍾鍿 +鎀鎁鎂鎃鎄鎅鎆鎇鎈鎉鎊鎋鎌鎍鎎鎏鎐鎑鎒鎓鎔鎕鎖鎗鎘鎙鎚鎛鎜鎝鎞鎟 +鎠鎡鎢鎣鎤鎥鎦鎧鎨鎩鎪鎫鎬鎭鎮鎯鎰鎱鎲鎳鎴鎵鎶鎷鎸鎹鎺鎻鎼鎽鎾鎿 +鏀鏁鏂鏃鏄鏅鏆鏇鏈鏉鏊鏋鏌鏍鏎鏏鏐鏑鏒鏓鏔鏕鏖鏗鏘鏙鏚鏛鏜鏝鏞鏟 +鏠鏡鏢鏣鏤鏥鏦鏧鏨鏩鏪鏫鏬鏭鏮鏯鏰鏱鏲鏳鏴鏵鏶鏷鏸鏹鏺鏻鏼鏽鏾鏿 +鐀鐁鐂鐃鐄鐅鐆鐇鐈鐉鐊鐋鐌鐍鐎鐏鐐鐑鐒鐓鐔鐕鐖鐗鐘鐙鐚鐛鐜鐝鐞鐟 +鐠鐡鐢鐣鐤鐥鐦鐧鐨鐩鐪鐫鐬鐭鐮鐯鐰鐱鐲鐳鐴鐵鐶鐷鐸鐹鐺鐻鐼鐽鐾鐿 +鑀鑁鑂鑃鑄鑅鑆鑇鑈鑉鑊鑋鑌鑍鑎鑏鑐鑑鑒鑓鑔鑕鑖鑗鑘鑙鑚鑛鑜鑝鑞鑟 +鑠鑡鑢鑣鑤鑥鑦鑧鑨鑩鑪鑫鑬鑭鑮鑯鑰鑱鑲鑳鑴鑵鑶鑷鑸鑹鑺鑻鑼鑽鑾鑿 +钀钁钂钃钄钅钆钇针钉钊钋钌钍钎钏钐钑钒钓钔钕钖钗钘钙钚钛钜钝钞钟 +钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿 +铀铁铂铃铄铅铆铇铈铉铊铋铌铍铎铏铐铑铒铓铔铕铖铗铘铙铚铛铜铝铞铟 +铠铡铢铣铤铥铦铧铨铩铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿 +销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗锘错锚锛锜锝锞锟 +锠锡锢锣锤锥锦锧锨锩锪锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿 +镀镁镂镃镄镅镆镇镈镉镊镋镌镍镎镏镐镑镒镓镔镕镖镗镘镙镚镛镜镝镞镟 +镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镵镶長镸镹镺镻镼镽镾长 +門閁閂閃閄閅閆閇閈閉閊開閌閍閎閏閐閑閒間閔閕閖閗閘閙閚閛閜閝閞閟 +閠閡関閣閤閥閦閧閨閩閪閫閬閭閮閯閰閱閲閳閴閵閶閷閸閹閺閻閼閽閾閿 +闀闁闂闃闄闅闆闇闈闉闊闋闌闍闎闏闐闑闒闓闔闕闖闗闘闙闚闛關闝闞闟 +闠闡闢闣闤闥闦闧门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿 +阀阁阂阃阄阅阆阇阈阉阊阋阌阍阎阏阐阑阒阓阔阕阖阗阘阙阚阛阜阝阞队 +阠阡阢阣阤阥阦阧阨阩阪阫阬阭阮阯阰阱防阳阴阵阶阷阸阹阺阻阼阽阾阿 +陀陁陂陃附际陆陇陈陉陊陋陌降陎陏限陑陒陓陔陕陖陗陘陙陚陛陜陝陞陟 +陠陡院陣除陥陦陧陨险陪陫陬陭陮陯陰陱陲陳陴陵陶陷陸陹険陻陼陽陾陿 +隀隁隂隃隄隅隆隇隈隉隊隋隌隍階随隐隑隒隓隔隕隖隗隘隙隚際障隝隞隟 +隠隡隢隣隤隥隦隧隨隩險隫隬隭隮隯隰隱隲隳隴隵隶隷隸隹隺隻隼隽难隿 +雀雁雂雃雄雅集雇雈雉雊雋雌雍雎雏雐雑雒雓雔雕雖雗雘雙雚雛雜雝雞雟 +雠雡離難雤雥雦雧雨雩雪雫雬雭雮雯雰雱雲雳雴雵零雷雸雹雺電雼雽雾雿 +需霁霂霃霄霅霆震霈霉霊霋霌霍霎霏霐霑霒霓霔霕霖霗霘霙霚霛霜霝霞霟 +霠霡霢霣霤霥霦霧霨霩霪霫霬霭霮霯霰霱露霳霴霵霶霷霸霹霺霻霼霽霾霿 +靀靁靂靃靄靅靆靇靈靉靊靋靌靍靎靏靐靑青靓靔靕靖靗靘静靚靛靜靝非靟 +靠靡面靣靤靥靦靧靨革靪靫靬靭靮靯靰靱靲靳靴靵靶靷靸靹靺靻靼靽靾靿 +鞀鞁鞂鞃鞄鞅鞆鞇鞈鞉鞊鞋鞌鞍鞎鞏鞐鞑鞒鞓鞔鞕鞖鞗鞘鞙鞚鞛鞜鞝鞞鞟 +鞠鞡鞢鞣鞤鞥鞦鞧鞨鞩鞪鞫鞬鞭鞮鞯鞰鞱鞲鞳鞴鞵鞶鞷鞸鞹鞺鞻鞼鞽鞾鞿 +韀韁韂韃韄韅韆韇韈韉韊韋韌韍韎韏韐韑韒韓韔韕韖韗韘韙韚韛韜韝韞韟 +韠韡韢韣韤韥韦韧韨韩韪韫韬韭韮韯韰韱韲音韴韵韶韷韸韹韺韻韼韽韾響 +頀頁頂頃頄項順頇須頉頊頋頌頍頎頏預頑頒頓頔頕頖頗領頙頚頛頜頝頞頟 +頠頡頢頣頤頥頦頧頨頩頪頫頬頭頮頯頰頱頲頳頴頵頶頷頸頹頺頻頼頽頾頿 +顀顁顂顃顄顅顆顇顈顉顊顋題額顎顏顐顑顒顓顔顕顖顗願顙顚顛顜顝類顟 +顠顡顢顣顤顥顦顧顨顩顪顫顬顭顮顯顰顱顲顳顴页顶顷顸项顺须顼顽顾顿 +颀颁颂颃预颅领颇颈颉颊颋颌颍颎颏颐频颒颓颔颕颖颗题颙颚颛颜额颞颟 +颠颡颢颣颤颥颦颧風颩颪颫颬颭颮颯颰颱颲颳颴颵颶颷颸颹颺颻颼颽颾颿 +飀飁飂飃飄飅飆飇飈飉飊飋飌飍风飏飐飑飒飓飔飕飖飗飘飙飚飛飜飝飞食 +飠飡飢飣飤飥飦飧飨飩飪飫飬飭飮飯飰飱飲飳飴飵飶飷飸飹飺飻飼飽飾飿 +餀餁餂餃餄餅餆餇餈餉養餋餌餍餎餏餐餑餒餓餔餕餖餗餘餙餚餛餜餝餞餟 +餠餡餢餣餤餥餦餧館餩餪餫餬餭餮餯餰餱餲餳餴餵餶餷餸餹餺餻餼餽餾餿 +饀饁饂饃饄饅饆饇饈饉饊饋饌饍饎饏饐饑饒饓饔饕饖饗饘饙饚饛饜饝饞饟 +饠饡饢饣饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿 +馀馁馂馃馄馅馆馇馈馉馊馋馌馍馎馏馐馑馒馓馔馕首馗馘香馚馛馜馝馞馟 +馠馡馢馣馤馥馦馧馨馩馪馫馬馭馮馯馰馱馲馳馴馵馶馷馸馹馺馻馼馽馾馿 +駀駁駂駃駄駅駆駇駈駉駊駋駌駍駎駏駐駑駒駓駔駕駖駗駘駙駚駛駜駝駞駟 +駠駡駢駣駤駥駦駧駨駩駪駫駬駭駮駯駰駱駲駳駴駵駶駷駸駹駺駻駼駽駾駿 +騀騁騂騃騄騅騆騇騈騉騊騋騌騍騎騏騐騑騒験騔騕騖騗騘騙騚騛騜騝騞騟 +騠騡騢騣騤騥騦騧騨騩騪騫騬騭騮騯騰騱騲騳騴騵騶騷騸騹騺騻騼騽騾騿 +驀驁驂驃驄驅驆驇驈驉驊驋驌驍驎驏驐驑驒驓驔驕驖驗驘驙驚驛驜驝驞驟 +驠驡驢驣驤驥驦驧驨驩驪驫马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿 +骀骁骂骃骄骅骆骇骈骉骊骋验骍骎骏骐骑骒骓骔骕骖骗骘骙骚骛骜骝骞骟 +骠骡骢骣骤骥骦骧骨骩骪骫骬骭骮骯骰骱骲骳骴骵骶骷骸骹骺骻骼骽骾骿 +髀髁髂髃髄髅髆髇髈髉髊髋髌髍髎髏髐髑髒髓體髕髖髗高髙髚髛髜髝髞髟 +髠髡髢髣髤髥髦髧髨髩髪髫髬髭髮髯髰髱髲髳髴髵髶髷髸髹髺髻髼髽髾髿 +鬀鬁鬂鬃鬄鬅鬆鬇鬈鬉鬊鬋鬌鬍鬎鬏鬐鬑鬒鬓鬔鬕鬖鬗鬘鬙鬚鬛鬜鬝鬞鬟 +鬠鬡鬢鬣鬤鬥鬦鬧鬨鬩鬪鬫鬬鬭鬮鬯鬰鬱鬲鬳鬴鬵鬶鬷鬸鬹鬺鬻鬼鬽鬾鬿 +魀魁魂魃魄魅魆魇魈魉魊魋魌魍魎魏魐魑魒魓魔魕魖魗魘魙魚魛魜魝魞魟 +魠魡魢魣魤魥魦魧魨魩魪魫魬魭魮魯魰魱魲魳魴魵魶魷魸魹魺魻魼魽魾魿 +鮀鮁鮂鮃鮄鮅鮆鮇鮈鮉鮊鮋鮌鮍鮎鮏鮐鮑鮒鮓鮔鮕鮖鮗鮘鮙鮚鮛鮜鮝鮞鮟 +鮠鮡鮢鮣鮤鮥鮦鮧鮨鮩鮪鮫鮬鮭鮮鮯鮰鮱鮲鮳鮴鮵鮶鮷鮸鮹鮺鮻鮼鮽鮾鮿 +鯀鯁鯂鯃鯄鯅鯆鯇鯈鯉鯊鯋鯌鯍鯎鯏鯐鯑鯒鯓鯔鯕鯖鯗鯘鯙鯚鯛鯜鯝鯞鯟 +鯠鯡鯢鯣鯤鯥鯦鯧鯨鯩鯪鯫鯬鯭鯮鯯鯰鯱鯲鯳鯴鯵鯶鯷鯸鯹鯺鯻鯼鯽鯾鯿 +鰀鰁鰂鰃鰄鰅鰆鰇鰈鰉鰊鰋鰌鰍鰎鰏鰐鰑鰒鰓鰔鰕鰖鰗鰘鰙鰚鰛鰜鰝鰞鰟 +鰠鰡鰢鰣鰤鰥鰦鰧鰨鰩鰪鰫鰬鰭鰮鰯鰰鰱鰲鰳鰴鰵鰶鰷鰸鰹鰺鰻鰼鰽鰾鰿 +鱀鱁鱂鱃鱄鱅鱆鱇鱈鱉鱊鱋鱌鱍鱎鱏鱐鱑鱒鱓鱔鱕鱖鱗鱘鱙鱚鱛鱜鱝鱞鱟 +鱠鱡鱢鱣鱤鱥鱦鱧鱨鱩鱪鱫鱬鱭鱮鱯鱰鱱鱲鱳鱴鱵鱶鱷鱸鱹鱺鱻鱼鱽鱾鱿 +鲀鲁鲂鲃鲄鲅鲆鲇鲈鲉鲊鲋鲌鲍鲎鲏鲐鲑鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜鲝鲞鲟 +鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿 +鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌鳍鳎鳏鳐鳑鳒鳓鳔鳕鳖鳗鳘鳙鳚鳛鳜鳝鳞鳟 +鳠鳡鳢鳣鳤鳥鳦鳧鳨鳩鳪鳫鳬鳭鳮鳯鳰鳱鳲鳳鳴鳵鳶鳷鳸鳹鳺鳻鳼鳽鳾鳿 +鴀鴁鴂鴃鴄鴅鴆鴇鴈鴉鴊鴋鴌鴍鴎鴏鴐鴑鴒鴓鴔鴕鴖鴗鴘鴙鴚鴛鴜鴝鴞鴟 +鴠鴡鴢鴣鴤鴥鴦鴧鴨鴩鴪鴫鴬鴭鴮鴯鴰鴱鴲鴳鴴鴵鴶鴷鴸鴹鴺鴻鴼鴽鴾鴿 +鵀鵁鵂鵃鵄鵅鵆鵇鵈鵉鵊鵋鵌鵍鵎鵏鵐鵑鵒鵓鵔鵕鵖鵗鵘鵙鵚鵛鵜鵝鵞鵟 +鵠鵡鵢鵣鵤鵥鵦鵧鵨鵩鵪鵫鵬鵭鵮鵯鵰鵱鵲鵳鵴鵵鵶鵷鵸鵹鵺鵻鵼鵽鵾鵿 +鶀鶁鶂鶃鶄鶅鶆鶇鶈鶉鶊鶋鶌鶍鶎鶏鶐鶑鶒鶓鶔鶕鶖鶗鶘鶙鶚鶛鶜鶝鶞鶟 +鶠鶡鶢鶣鶤鶥鶦鶧鶨鶩鶪鶫鶬鶭鶮鶯鶰鶱鶲鶳鶴鶵鶶鶷鶸鶹鶺鶻鶼鶽鶾鶿 +鷀鷁鷂鷃鷄鷅鷆鷇鷈鷉鷊鷋鷌鷍鷎鷏鷐鷑鷒鷓鷔鷕鷖鷗鷘鷙鷚鷛鷜鷝鷞鷟 +鷠鷡鷢鷣鷤鷥鷦鷧鷨鷩鷪鷫鷬鷭鷮鷯鷰鷱鷲鷳鷴鷵鷶鷷鷸鷹鷺鷻鷼鷽鷾鷿 +鸀鸁鸂鸃鸄鸅鸆鸇鸈鸉鸊鸋鸌鸍鸎鸏鸐鸑鸒鸓鸔鸕鸖鸗鸘鸙鸚鸛鸜鸝鸞鸟 +鸠鸡鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿 +鹀鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹍鹎鹏鹐鹑鹒鹓鹔鹕鹖鹗鹘鹙鹚鹛鹜鹝鹞鹟 +鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹮鹯鹰鹱鹲鹳鹴鹵鹶鹷鹸鹹鹺鹻鹼鹽鹾鹿 +麀麁麂麃麄麅麆麇麈麉麊麋麌麍麎麏麐麑麒麓麔麕麖麗麘麙麚麛麜麝麞麟 +麠麡麢麣麤麥麦麧麨麩麪麫麬麭麮麯麰麱麲麳麴麵麶麷麸麹麺麻麼麽麾麿 +黀黁黂黃黄黅黆黇黈黉黊黋黌黍黎黏黐黑黒黓黔黕黖黗默黙黚黛黜黝點黟 +黠黡黢黣黤黥黦黧黨黩黪黫黬黭黮黯黰黱黲黳黴黵黶黷黸黹黺黻黼黽黾黿 +鼀鼁鼂鼃鼄鼅鼆鼇鼈鼉鼊鼋鼌鼍鼎鼏鼐鼑鼒鼓鼔鼕鼖鼗鼘鼙鼚鼛鼜鼝鼞鼟 +鼠鼡鼢鼣鼤鼥鼦鼧鼨鼩鼪鼫鼬鼭鼮鼯鼰鼱鼲鼳鼴鼵鼶鼷鼸鼹鼺鼻鼼鼽鼾鼿 +齀齁齂齃齄齅齆齇齈齉齊齋齌齍齎齏齐齑齒齓齔齕齖齗齘齙齚齛齜齝齞齟 +齠齡齢齣齤齥齦齧齨齩齪齫齬齭齮齯齰齱齲齳齴齵齶齷齸齹齺齻齼齽齾齿 +龀龁龂龃龄龅龆龇龈龉龊龋龌龍龎龏龐龑龒龓龔龕龖龗龘龙龚龛龜龝龞龟 +龠龡龢龣龤龥龦龧龨龩龪龫龬龭龮龯龰龱龲龳龴龵龶龷龸龹龺龻龼龽龾龿 +鿀鿁鿂鿃鿄鿅鿆鿇鿈鿉鿊鿋鿌鿍鿎鿏鿐鿑鿒鿓鿔鿕鿖鿗鿘鿙鿚鿛鿜鿝鿞鿟 +鿠鿡鿢鿣鿤鿥鿦鿧鿨鿩鿪鿫鿬鿭鿮鿯鿰鿱鿲鿳鿴鿵鿶鿷鿸鿹鿺鿻鿼鿽鿾鿿 + +Yi Syllables (U+A000-U+A48F): + +ꀀꀁꀂꀃꀄꀅꀆꀇꀈꀉꀊꀋꀌꀍꀎꀏꀐꀑꀒꀓꀔꀕꀖꀗꀘꀙꀚꀛꀜꀝꀞꀟ +ꀠꀡꀢꀣꀤꀥꀦꀧꀨꀩꀪꀫꀬꀭꀮꀯꀰꀱꀲꀳꀴꀵꀶꀷꀸꀹꀺꀻꀼꀽꀾꀿ +ꁀꁁꁂꁃꁄꁅꁆꁇꁈꁉꁊꁋꁌꁍꁎꁏꁐꁑꁒꁓꁔꁕꁖꁗꁘꁙꁚꁛꁜꁝꁞꁟ +ꁠꁡꁢꁣꁤꁥꁦꁧꁨꁩꁪꁫꁬꁭꁮꁯꁰꁱꁲꁳꁴꁵꁶꁷꁸꁹꁺꁻꁼꁽꁾꁿ +ꂀꂁꂂꂃꂄꂅꂆꂇꂈꂉꂊꂋꂌꂍꂎꂏꂐꂑꂒꂓꂔꂕꂖꂗꂘꂙꂚꂛꂜꂝꂞꂟ +ꂠꂡꂢꂣꂤꂥꂦꂧꂨꂩꂪꂫꂬꂭꂮꂯꂰꂱꂲꂳꂴꂵꂶꂷꂸꂹꂺꂻꂼꂽꂾꂿ +ꃀꃁꃂꃃꃄꃅꃆꃇꃈꃉꃊꃋꃌꃍꃎꃏꃐꃑꃒꃓꃔꃕꃖꃗꃘꃙꃚꃛꃜꃝꃞꃟ +ꃠꃡꃢꃣꃤꃥꃦꃧꃨꃩꃪꃫꃬꃭꃮꃯꃰꃱꃲꃳꃴꃵꃶꃷꃸꃹꃺꃻꃼꃽꃾꃿ +ꄀꄁꄂꄃꄄꄅꄆꄇꄈꄉꄊꄋꄌꄍꄎꄏꄐꄑꄒꄓꄔꄕꄖꄗꄘꄙꄚꄛꄜꄝꄞꄟ +ꄠꄡꄢꄣꄤꄥꄦꄧꄨꄩꄪꄫꄬꄭꄮꄯꄰꄱꄲꄳꄴꄵꄶꄷꄸꄹꄺꄻꄼꄽꄾꄿ +ꅀꅁꅂꅃꅄꅅꅆꅇꅈꅉꅊꅋꅌꅍꅎꅏꅐꅑꅒꅓꅔꅕꅖꅗꅘꅙꅚꅛꅜꅝꅞꅟ +ꅠꅡꅢꅣꅤꅥꅦꅧꅨꅩꅪꅫꅬꅭꅮꅯꅰꅱꅲꅳꅴꅵꅶꅷꅸꅹꅺꅻꅼꅽꅾꅿ +ꆀꆁꆂꆃꆄꆅꆆꆇꆈꆉꆊꆋꆌꆍꆎꆏꆐꆑꆒꆓꆔꆕꆖꆗꆘꆙꆚꆛꆜꆝꆞꆟ +ꆠꆡꆢꆣꆤꆥꆦꆧꆨꆩꆪꆫꆬꆭꆮꆯꆰꆱꆲꆳꆴꆵꆶꆷꆸꆹꆺꆻꆼꆽꆾꆿ +ꇀꇁꇂꇃꇄꇅꇆꇇꇈꇉꇊꇋꇌꇍꇎꇏꇐꇑꇒꇓꇔꇕꇖꇗꇘꇙꇚꇛꇜꇝꇞꇟ +ꇠꇡꇢꇣꇤꇥꇦꇧꇨꇩꇪꇫꇬꇭꇮꇯꇰꇱꇲꇳꇴꇵꇶꇷꇸꇹꇺꇻꇼꇽꇾꇿ +ꈀꈁꈂꈃꈄꈅꈆꈇꈈꈉꈊꈋꈌꈍꈎꈏꈐꈑꈒꈓꈔꈕꈖꈗꈘꈙꈚꈛꈜꈝꈞꈟ +ꈠꈡꈢꈣꈤꈥꈦꈧꈨꈩꈪꈫꈬꈭꈮꈯꈰꈱꈲꈳꈴꈵꈶꈷꈸꈹꈺꈻꈼꈽꈾꈿ +ꉀꉁꉂꉃꉄꉅꉆꉇꉈꉉꉊꉋꉌꉍꉎꉏꉐꉑꉒꉓꉔꉕꉖꉗꉘꉙꉚꉛꉜꉝꉞꉟ +ꉠꉡꉢꉣꉤꉥꉦꉧꉨꉩꉪꉫꉬꉭꉮꉯꉰꉱꉲꉳꉴꉵꉶꉷꉸꉹꉺꉻꉼꉽꉾꉿ +ꊀꊁꊂꊃꊄꊅꊆꊇꊈꊉꊊꊋꊌꊍꊎꊏꊐꊑꊒꊓꊔꊕꊖꊗꊘꊙꊚꊛꊜꊝꊞꊟ +ꊠꊡꊢꊣꊤꊥꊦꊧꊨꊩꊪꊫꊬꊭꊮꊯꊰꊱꊲꊳꊴꊵꊶꊷꊸꊹꊺꊻꊼꊽꊾꊿ +ꋀꋁꋂꋃꋄꋅꋆꋇꋈꋉꋊꋋꋌꋍꋎꋏꋐꋑꋒꋓꋔꋕꋖꋗꋘꋙꋚꋛꋜꋝꋞꋟ +ꋠꋡꋢꋣꋤꋥꋦꋧꋨꋩꋪꋫꋬꋭꋮꋯꋰꋱꋲꋳꋴꋵꋶꋷꋸꋹꋺꋻꋼꋽꋾꋿ +ꌀꌁꌂꌃꌄꌅꌆꌇꌈꌉꌊꌋꌌꌍꌎꌏꌐꌑꌒꌓꌔꌕꌖꌗꌘꌙꌚꌛꌜꌝꌞꌟ +ꌠꌡꌢꌣꌤꌥꌦꌧꌨꌩꌪꌫꌬꌭꌮꌯꌰꌱꌲꌳꌴꌵꌶꌷꌸꌹꌺꌻꌼꌽꌾꌿ +ꍀꍁꍂꍃꍄꍅꍆꍇꍈꍉꍊꍋꍌꍍꍎꍏꍐꍑꍒꍓꍔꍕꍖꍗꍘꍙꍚꍛꍜꍝꍞꍟ +ꍠꍡꍢꍣꍤꍥꍦꍧꍨꍩꍪꍫꍬꍭꍮꍯꍰꍱꍲꍳꍴꍵꍶꍷꍸꍹꍺꍻꍼꍽꍾꍿ +ꎀꎁꎂꎃꎄꎅꎆꎇꎈꎉꎊꎋꎌꎍꎎꎏꎐꎑꎒꎓꎔꎕꎖꎗꎘꎙꎚꎛꎜꎝꎞꎟ +ꎠꎡꎢꎣꎤꎥꎦꎧꎨꎩꎪꎫꎬꎭꎮꎯꎰꎱꎲꎳꎴꎵꎶꎷꎸꎹꎺꎻꎼꎽꎾꎿ +ꏀꏁꏂꏃꏄꏅꏆꏇꏈꏉꏊꏋꏌꏍꏎꏏꏐꏑꏒꏓꏔꏕꏖꏗꏘꏙꏚꏛꏜꏝꏞꏟ +ꏠꏡꏢꏣꏤꏥꏦꏧꏨꏩꏪꏫꏬꏭꏮꏯꏰꏱꏲꏳꏴꏵꏶꏷꏸꏹꏺꏻꏼꏽꏾꏿ +ꐀꐁꐂꐃꐄꐅꐆꐇꐈꐉꐊꐋꐌꐍꐎꐏꐐꐑꐒꐓꐔꐕꐖꐗꐘꐙꐚꐛꐜꐝꐞꐟ +ꐠꐡꐢꐣꐤꐥꐦꐧꐨꐩꐪꐫꐬꐭꐮꐯꐰꐱꐲꐳꐴꐵꐶꐷꐸꐹꐺꐻꐼꐽꐾꐿ +ꑀꑁꑂꑃꑄꑅꑆꑇꑈꑉꑊꑋꑌꑍꑎꑏꑐꑑꑒꑓꑔꑕꑖꑗꑘꑙꑚꑛꑜꑝꑞꑟ +ꑠꑡꑢꑣꑤꑥꑦꑧꑨꑩꑪꑫꑬꑭꑮꑯꑰꑱꑲꑳꑴꑵꑶꑷꑸꑹꑺꑻꑼꑽꑾꑿ +ꒀꒁꒂꒃꒄꒅꒆꒇꒈꒉꒊꒋꒌ꒍꒎꒏ + +Yi Radicals (U+A490-U+A4CF): + +꒐꒑꒒꒓꒔꒕꒖꒗꒘꒙꒚꒛꒜꒝꒞꒟꒠꒡꒢꒣꒤꒥꒦꒧꒨꒩꒪꒫꒬꒭꒮꒯ +꒰꒱꒲꒳꒴꒵꒶꒷꒸꒹꒺꒻꒼꒽꒾꒿꓀꓁꓂꓃꓄꓅꓆꓇꓈꓉꓊꓋꓌꓍꓎꓏ + +Free block (U+A4D0-U+ABFF): + +ꓐꓑꓒꓓꓔꓕꓖꓗꓘꓙꓚꓛꓜꓝꓞꓟꓠꓡꓢꓣꓤꓥꓦꓧꓨꓩꓪꓫꓬꓭꓮꓯꓰꓱꓲꓳꓴꓵꓶꓷꓸꓹꓺꓻꓼꓽ꓾꓿ꔀꔁꔂꔃꔄꔅꔆꔇꔈꔉꔊꔋꔌꔍꔎꔏ +ꔐꔑꔒꔓꔔꔕꔖꔗꔘꔙꔚꔛꔜꔝꔞꔟꔠꔡꔢꔣꔤꔥꔦꔧꔨꔩꔪꔫꔬꔭꔮꔯꔰꔱꔲꔳꔴꔵꔶꔷꔸꔹꔺꔻꔼꔽꔾꔿꕀꕁꕂꕃꕄꕅꕆꕇꕈꕉꕊꕋꕌꕍꕎꕏ +ꕐꕑꕒꕓꕔꕕꕖꕗꕘꕙꕚꕛꕜꕝꕞꕟꕠꕡꕢꕣꕤꕥꕦꕧꕨꕩꕪꕫꕬꕭꕮꕯꕰꕱꕲꕳꕴꕵꕶꕷꕸꕹꕺꕻꕼꕽꕾꕿꖀꖁꖂꖃꖄꖅꖆꖇꖈꖉꖊꖋꖌꖍꖎꖏ +ꖐꖑꖒꖓꖔꖕꖖꖗꖘꖙꖚꖛꖜꖝꖞꖟꖠꖡꖢꖣꖤꖥꖦꖧꖨꖩꖪꖫꖬꖭꖮꖯꖰꖱꖲꖳꖴꖵꖶꖷꖸꖹꖺꖻꖼꖽꖾꖿꗀꗁꗂꗃꗄꗅꗆꗇꗈꗉꗊꗋꗌꗍꗎꗏ +ꗐꗑꗒꗓꗔꗕꗖꗗꗘꗙꗚꗛꗜꗝꗞꗟꗠꗡꗢꗣꗤꗥꗦꗧꗨꗩꗪꗫꗬꗭꗮꗯꗰꗱꗲꗳꗴꗵꗶꗷꗸꗹꗺꗻꗼꗽꗾꗿꘀꘁꘂꘃꘄꘅꘆꘇꘈꘉꘊꘋꘌ꘍꘎꘏ +ꘐꘑꘒꘓꘔꘕꘖꘗꘘꘙꘚꘛꘜꘝꘞꘟ꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩ꘪꘫ꘬꘭꘮꘯꘰꘱꘲꘳꘴꘵꘶꘷꘸꘹꘺꘻꘼꘽꘾꘿ꙀꙁꙂꙃꙄꙅꙆꙇꙈꙉꙊꙋꙌꙍꙎꙏ +ꙐꙑꙒꙓꙔꙕꙖꙗꙘꙙꙚꙛꙜꙝꙞꙟꙠꙡꙢꙣꙤꙥꙦꙧꙨꙩꙪꙫꙬꙭꙮ꙯꙰꙱꙲꙳ꙴꙵꙶꙷꙸꙹꙺꙻ꙼꙽꙾ꙿꚀꚁꚂꚃꚄꚅꚆꚇꚈꚉꚊꚋꚌꚍꚎꚏ +ꚐꚑꚒꚓꚔꚕꚖꚗꚘꚙꚚꚛꚜꚝꚞꚟꚠꚡꚢꚣꚤꚥꚦꚧꚨꚩꚪꚫꚬꚭꚮꚯꚰꚱꚲꚳꚴꚵꚶꚷꚸꚹꚺꚻꚼꚽꚾꚿꛀꛁꛂꛃꛄꛅꛆꛇꛈꛉꛊꛋꛌꛍꛎꛏ +ꛐꛑꛒꛓꛔꛕꛖꛗꛘꛙꛚꛛꛜꛝꛞꛟꛠꛡꛢꛣꛤꛥꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ꛰꛱꛲꛳꛴꛵꛶꛷꛸꛹꛺꛻꛼꛽꛾꛿꜀꜁꜂꜃꜄꜅꜆꜇꜈꜉꜊꜋꜌꜍꜎꜏ +꜐꜑꜒꜓꜔꜕꜖ꜗꜘꜙꜚꜛꜜꜝꜞꜟ꜠꜡ꜢꜣꜤꜥꜦꜧꜨꜩꜪꜫꜬꜭꜮꜯꜰꜱꜲꜳꜴꜵꜶꜷꜸꜹꜺꜻꜼꜽꜾꜿꝀꝁꝂꝃꝄꝅꝆꝇꝈꝉꝊꝋꝌꝍꝎꝏ +ꝐꝑꝒꝓꝔꝕꝖꝗꝘꝙꝚꝛꝜꝝꝞꝟꝠꝡꝢꝣꝤꝥꝦꝧꝨꝩꝪꝫꝬꝭꝮꝯꝰꝱꝲꝳꝴꝵꝶꝷꝸꝹꝺꝻꝼꝽꝾꝿꞀꞁꞂꞃꞄꞅꞆꞇꞈ꞉꞊ꞋꞌꞍꞎꞏ +ꞐꞑꞒꞓꞔꞕꞖꞗꞘꞙꞚꞛꞜꞝꞞꞟꞠꞡꞢꞣꞤꞥꞦꞧꞨꞩꞪꞫꞬꞭꞮꞯꞰꞱꞲꞳꞴꞵꞶꞷꞸꞹꞺꞻꞼꞽꞾꞿꟀꟁꟂꟃꟄꟅꟆꟇꟈꟉꟊꟋꟌꟍ꟎꟏ +Ꟑꟑ꟒ꟓ꟔ꟕꟖꟗꟘꟙꟚꟛꟜ꟝꟞꟟꟠꟡꟢꟣꟤꟥꟦꟧꟨꟩꟪꟫꟬꟭꟮꟯꟰꟱ꟲꟳꟴꟵꟶꟷꟸꟹꟺꟻꟼꟽꟾꟿꠀꠁꠂꠃꠄꠅ꠆ꠇꠈꠉꠊꠋꠌꠍꠎꠏ +ꠐꠑꠒꠓꠔꠕꠖꠗꠘꠙꠚꠛꠜꠝꠞꠟꠠꠡꠢꠣꠤꠥꠦꠧ꠨꠩꠪꠫꠬꠭꠮꠯꠰꠱꠲꠳꠴꠵꠶꠷꠸꠹꠺꠻꠼꠽꠾꠿ꡀꡁꡂꡃꡄꡅꡆꡇꡈꡉꡊꡋꡌꡍꡎꡏ +ꡐꡑꡒꡓꡔꡕꡖꡗꡘꡙꡚꡛꡜꡝꡞꡟꡠꡡꡢꡣꡤꡥꡦꡧꡨꡩꡪꡫꡬꡭꡮꡯꡰꡱꡲꡳ꡴꡵꡶꡷꡸꡹꡺꡻꡼꡽꡾꡿ꢀꢁꢂꢃꢄꢅꢆꢇꢈꢉꢊꢋꢌꢍꢎꢏ +ꢐꢑꢒꢓꢔꢕꢖꢗꢘꢙꢚꢛꢜꢝꢞꢟꢠꢡꢢꢣꢤꢥꢦꢧꢨꢩꢪꢫꢬꢭꢮꢯꢰꢱꢲꢳꢴꢵꢶꢷꢸꢹꢺꢻꢼꢽꢾꢿꣀꣁꣂꣃ꣄ꣅ꣆꣇꣈꣉꣊꣋꣌꣍꣎꣏ +꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꣚꣛꣜꣝꣞꣟꣠꣡꣢꣣꣤꣥꣦꣧꣨꣩꣪꣫꣬꣭꣮꣯꣰꣱ꣲꣳꣴꣵꣶꣷ꣸꣹꣺ꣻ꣼ꣽꣾꣿ꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉ꤊꤋꤌꤍꤎꤏ +ꤐꤑꤒꤓꤔꤕꤖꤗꤘꤙꤚꤛꤜꤝꤞꤟꤠꤡꤢꤣꤤꤥꤦꤧꤨꤩꤪ꤫꤬꤭꤮꤯ꤰꤱꤲꤳꤴꤵꤶꤷꤸꤹꤺꤻꤼꤽꤾꤿꥀꥁꥂꥃꥄꥅꥆꥇꥈꥉꥊꥋꥌꥍꥎꥏ +ꥐꥑꥒ꥓꥔꥕꥖꥗꥘꥙꥚꥛꥜꥝꥞꥟ꥠꥡꥢꥣꥤꥥꥦꥧꥨꥩꥪꥫꥬꥭꥮꥯꥰꥱꥲꥳꥴꥵꥶꥷꥸꥹꥺꥻꥼ꥽꥾꥿ꦀꦁꦂꦃꦄꦅꦆꦇꦈꦉꦊꦋꦌꦍꦎꦏ +ꦐꦑꦒꦓꦔꦕꦖꦗꦘꦙꦚꦛꦜꦝꦞꦟꦠꦡꦢꦣꦤꦥꦦꦧꦨꦩꦪꦫꦬꦭꦮꦯꦰꦱꦲ꦳ꦴꦵꦶꦷꦸꦹꦺꦻꦼꦽꦾꦿ꧀꧁꧂꧃꧄꧅꧆꧇꧈꧉꧊꧋꧌꧍꧎ꧏ +꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙꧚꧛꧜꧝꧞꧟ꧠꧡꧢꧣꧤꧥꧦꧧꧨꧩꧪꧫꧬꧭꧮꧯ꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹ꧺꧻꧼꧽꧾ꧿ꨀꨁꨂꨃꨄꨅꨆꨇꨈꨉꨊꨋꨌꨍꨎꨏ +ꨐꨑꨒꨓꨔꨕꨖꨗꨘꨙꨚꨛꨜꨝꨞꨟꨠꨡꨢꨣꨤꨥꨦꨧꨨꨩꨪꨫꨬꨭꨮꨯꨰꨱꨲꨳꨴꨵꨶ꨷꨸꨹꨺꨻꨼꨽꨾꨿ꩀꩁꩂꩃꩄꩅꩆꩇꩈꩉꩊꩋꩌꩍ꩎꩏ +꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙꩚꩛꩜꩝꩞꩟ꩠꩡꩢꩣꩤꩥꩦꩧꩨꩩꩪꩫꩬꩭꩮꩯꩰꩱꩲꩳꩴꩵꩶ꩷꩸꩹ꩺꩻꩼꩽꩾꩿꪀꪁꪂꪃꪄꪅꪆꪇꪈꪉꪊꪋꪌꪍꪎꪏ +ꪐꪑꪒꪓꪔꪕꪖꪗꪘꪙꪚꪛꪜꪝꪞꪟꪠꪡꪢꪣꪤꪥꪦꪧꪨꪩꪪꪫꪬꪭꪮꪯꪰꪱꪴꪲꪳꪵꪶꪷꪸꪹꪺꪻꪼꪽꪾ꪿ꫀ꫁ꫂ꫃꫄꫅꫆꫇꫈꫉꫊꫋꫌꫍꫎꫏ +꫐꫑꫒꫓꫔꫕꫖꫗꫘꫙꫚ꫛꫜꫝ꫞꫟ꫠꫡꫢꫣꫤꫥꫦꫧꫨꫩꫪꫫꫬꫭꫮꫯ꫰꫱ꫲꫳꫴꫵ꫶꫷꫸꫹꫺꫻꫼꫽꫾꫿꬀ꬁꬂꬃꬄꬅꬆ꬇꬈ꬉꬊꬋꬌꬍꬎ꬏ +꬐ꬑꬒꬓꬔꬕꬖ꬗꬘꬙꬚꬛꬜꬝꬞꬟ꬠꬡꬢꬣꬤꬥꬦ꬧ꬨꬩꬪꬫꬬꬭꬮ꬯ꬰꬱꬲꬳꬴꬵꬶꬷꬸꬹꬺꬻꬼꬽꬾꬿꭀꭁꭂꭃꭄꭅꭆꭇꭈꭉꭊꭋꭌꭍꭎꭏ +ꭐꭑꭒꭓꭔꭕꭖꭗꭘꭙꭚ꭛ꭜꭝꭞꭟꭠꭡꭢꭣꭤꭥꭦꭧꭨꭩ꭪꭫꭬꭭꭮꭯ꭰꭱꭲꭳꭴꭵꭶꭷꭸꭹꭺꭻꭼꭽꭾꭿꮀꮁꮂꮃꮄꮅꮆꮇꮈꮉꮊꮋꮌꮍꮎꮏ +ꮐꮑꮒꮓꮔꮕꮖꮗꮘꮙꮚꮛꮜꮝꮞꮟꮠꮡꮢꮣꮤꮥꮦꮧꮨꮩꮪꮫꮬꮭꮮꮯꮰꮱꮲꮳꮴꮵꮶꮷꮸꮹꮺꮻꮼꮽꮾꮿꯀꯁꯂꯃꯄꯅꯆꯇꯈꯉꯊꯋꯌꯍꯎꯏ +ꯐꯑꯒꯓꯔꯕꯖꯗꯘꯙꯚꯛꯜꯝꯞꯟꯠꯡꯢꯣꯤꯥꯦꯧꯨꯩꯪ꯫꯬꯭꯮꯯꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹꯺꯻꯼꯽꯾꯿ + +Hangul Syllables (U+AC00-U+D7AF): + +가각갂갃간갅갆갇갈갉갊갋갌갍갎갏감갑값갓갔강갖갗갘같갚갛개객갞갟 +갠갡갢갣갤갥갦갧갨갩갪갫갬갭갮갯갰갱갲갳갴갵갶갷갸갹갺갻갼갽갾갿 +걀걁걂걃걄걅걆걇걈걉걊걋걌걍걎걏걐걑걒걓걔걕걖걗걘걙걚걛걜걝걞걟 +걠걡걢걣걤걥걦걧걨걩걪걫걬걭걮걯거걱걲걳건걵걶걷걸걹걺걻걼걽걾걿 +검겁겂것겄겅겆겇겈겉겊겋게겍겎겏겐겑겒겓겔겕겖겗겘겙겚겛겜겝겞겟 +겠겡겢겣겤겥겦겧겨격겪겫견겭겮겯결겱겲겳겴겵겶겷겸겹겺겻겼경겾겿 +곀곁곂곃계곅곆곇곈곉곊곋곌곍곎곏곐곑곒곓곔곕곖곗곘곙곚곛곜곝곞곟 +고곡곢곣곤곥곦곧골곩곪곫곬곭곮곯곰곱곲곳곴공곶곷곸곹곺곻과곽곾곿 +관괁괂괃괄괅괆괇괈괉괊괋괌괍괎괏괐광괒괓괔괕괖괗괘괙괚괛괜괝괞괟 +괠괡괢괣괤괥괦괧괨괩괪괫괬괭괮괯괰괱괲괳괴괵괶괷괸괹괺괻괼괽괾괿 +굀굁굂굃굄굅굆굇굈굉굊굋굌굍굎굏교굑굒굓굔굕굖굗굘굙굚굛굜굝굞굟 +굠굡굢굣굤굥굦굧굨굩굪굫구국굮굯군굱굲굳굴굵굶굷굸굹굺굻굼굽굾굿 +궀궁궂궃궄궅궆궇궈궉궊궋권궍궎궏궐궑궒궓궔궕궖궗궘궙궚궛궜궝궞궟 +궠궡궢궣궤궥궦궧궨궩궪궫궬궭궮궯궰궱궲궳궴궵궶궷궸궹궺궻궼궽궾궿 +귀귁귂귃귄귅귆귇귈귉귊귋귌귍귎귏귐귑귒귓귔귕귖귗귘귙귚귛규귝귞귟 +균귡귢귣귤귥귦귧귨귩귪귫귬귭귮귯귰귱귲귳귴귵귶귷그극귺귻근귽귾귿 +글긁긂긃긄긅긆긇금급긊긋긌긍긎긏긐긑긒긓긔긕긖긗긘긙긚긛긜긝긞긟 +긠긡긢긣긤긥긦긧긨긩긪긫긬긭긮긯기긱긲긳긴긵긶긷길긹긺긻긼긽긾긿 +김깁깂깃깄깅깆깇깈깉깊깋까깍깎깏깐깑깒깓깔깕깖깗깘깙깚깛깜깝깞깟 +깠깡깢깣깤깥깦깧깨깩깪깫깬깭깮깯깰깱깲깳깴깵깶깷깸깹깺깻깼깽깾깿 +꺀꺁꺂꺃꺄꺅꺆꺇꺈꺉꺊꺋꺌꺍꺎꺏꺐꺑꺒꺓꺔꺕꺖꺗꺘꺙꺚꺛꺜꺝꺞꺟 +꺠꺡꺢꺣꺤꺥꺦꺧꺨꺩꺪꺫꺬꺭꺮꺯꺰꺱꺲꺳꺴꺵꺶꺷꺸꺹꺺꺻꺼꺽꺾꺿 +껀껁껂껃껄껅껆껇껈껉껊껋껌껍껎껏껐껑껒껓껔껕껖껗께껙껚껛껜껝껞껟 +껠껡껢껣껤껥껦껧껨껩껪껫껬껭껮껯껰껱껲껳껴껵껶껷껸껹껺껻껼껽껾껿 +꼀꼁꼂꼃꼄꼅꼆꼇꼈꼉꼊꼋꼌꼍꼎꼏꼐꼑꼒꼓꼔꼕꼖꼗꼘꼙꼚꼛꼜꼝꼞꼟 +꼠꼡꼢꼣꼤꼥꼦꼧꼨꼩꼪꼫꼬꼭꼮꼯꼰꼱꼲꼳꼴꼵꼶꼷꼸꼹꼺꼻꼼꼽꼾꼿 +꽀꽁꽂꽃꽄꽅꽆꽇꽈꽉꽊꽋꽌꽍꽎꽏꽐꽑꽒꽓꽔꽕꽖꽗꽘꽙꽚꽛꽜꽝꽞꽟 +꽠꽡꽢꽣꽤꽥꽦꽧꽨꽩꽪꽫꽬꽭꽮꽯꽰꽱꽲꽳꽴꽵꽶꽷꽸꽹꽺꽻꽼꽽꽾꽿 +꾀꾁꾂꾃꾄꾅꾆꾇꾈꾉꾊꾋꾌꾍꾎꾏꾐꾑꾒꾓꾔꾕꾖꾗꾘꾙꾚꾛꾜꾝꾞꾟 +꾠꾡꾢꾣꾤꾥꾦꾧꾨꾩꾪꾫꾬꾭꾮꾯꾰꾱꾲꾳꾴꾵꾶꾷꾸꾹꾺꾻꾼꾽꾾꾿 +꿀꿁꿂꿃꿄꿅꿆꿇꿈꿉꿊꿋꿌꿍꿎꿏꿐꿑꿒꿓꿔꿕꿖꿗꿘꿙꿚꿛꿜꿝꿞꿟 +꿠꿡꿢꿣꿤꿥꿦꿧꿨꿩꿪꿫꿬꿭꿮꿯꿰꿱꿲꿳꿴꿵꿶꿷꿸꿹꿺꿻꿼꿽꿾꿿 +뀀뀁뀂뀃뀄뀅뀆뀇뀈뀉뀊뀋뀌뀍뀎뀏뀐뀑뀒뀓뀔뀕뀖뀗뀘뀙뀚뀛뀜뀝뀞뀟 +뀠뀡뀢뀣뀤뀥뀦뀧뀨뀩뀪뀫뀬뀭뀮뀯뀰뀱뀲뀳뀴뀵뀶뀷뀸뀹뀺뀻뀼뀽뀾뀿 +끀끁끂끃끄끅끆끇끈끉끊끋끌끍끎끏끐끑끒끓끔끕끖끗끘끙끚끛끜끝끞끟 +끠끡끢끣끤끥끦끧끨끩끪끫끬끭끮끯끰끱끲끳끴끵끶끷끸끹끺끻끼끽끾끿 +낀낁낂낃낄낅낆낇낈낉낊낋낌낍낎낏낐낑낒낓낔낕낖낗나낙낚낛난낝낞낟 +날낡낢낣낤낥낦낧남납낪낫났낭낮낯낰낱낲낳내낵낶낷낸낹낺낻낼낽낾낿 +냀냁냂냃냄냅냆냇냈냉냊냋냌냍냎냏냐냑냒냓냔냕냖냗냘냙냚냛냜냝냞냟 +냠냡냢냣냤냥냦냧냨냩냪냫냬냭냮냯냰냱냲냳냴냵냶냷냸냹냺냻냼냽냾냿 +넀넁넂넃넄넅넆넇너넉넊넋넌넍넎넏널넑넒넓넔넕넖넗넘넙넚넛넜넝넞넟 +넠넡넢넣네넥넦넧넨넩넪넫넬넭넮넯넰넱넲넳넴넵넶넷넸넹넺넻넼넽넾넿 +녀녁녂녃년녅녆녇녈녉녊녋녌녍녎녏념녑녒녓녔녕녖녗녘녙녚녛녜녝녞녟 +녠녡녢녣녤녥녦녧녨녩녪녫녬녭녮녯녰녱녲녳녴녵녶녷노녹녺녻논녽녾녿 +놀놁놂놃놄놅놆놇놈놉놊놋놌농놎놏놐놑높놓놔놕놖놗놘놙놚놛놜놝놞놟 +놠놡놢놣놤놥놦놧놨놩놪놫놬놭놮놯놰놱놲놳놴놵놶놷놸놹놺놻놼놽놾놿 +뇀뇁뇂뇃뇄뇅뇆뇇뇈뇉뇊뇋뇌뇍뇎뇏뇐뇑뇒뇓뇔뇕뇖뇗뇘뇙뇚뇛뇜뇝뇞뇟 +뇠뇡뇢뇣뇤뇥뇦뇧뇨뇩뇪뇫뇬뇭뇮뇯뇰뇱뇲뇳뇴뇵뇶뇷뇸뇹뇺뇻뇼뇽뇾뇿 +눀눁눂눃누눅눆눇눈눉눊눋눌눍눎눏눐눑눒눓눔눕눖눗눘눙눚눛눜눝눞눟 +눠눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿 +뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟 +뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿 +늀늁늂늃늄늅늆늇늈늉늊늋늌늍늎늏느늑늒늓는늕늖늗늘늙늚늛늜늝늞늟 +늠늡늢늣늤능늦늧늨늩늪늫늬늭늮늯늰늱늲늳늴늵늶늷늸늹늺늻늼늽늾늿 +닀닁닂닃닄닅닆닇니닉닊닋닌닍닎닏닐닑닒닓닔닕닖닗님닙닚닛닜닝닞닟 +닠닡닢닣다닥닦닧단닩닪닫달닭닮닯닰닱닲닳담답닶닷닸당닺닻닼닽닾닿 +대댁댂댃댄댅댆댇댈댉댊댋댌댍댎댏댐댑댒댓댔댕댖댗댘댙댚댛댜댝댞댟 +댠댡댢댣댤댥댦댧댨댩댪댫댬댭댮댯댰댱댲댳댴댵댶댷댸댹댺댻댼댽댾댿 +덀덁덂덃덄덅덆덇덈덉덊덋덌덍덎덏덐덑덒덓더덕덖덗던덙덚덛덜덝덞덟 +덠덡덢덣덤덥덦덧덨덩덪덫덬덭덮덯데덱덲덳덴덵덶덷델덹덺덻덼덽덾덿 +뎀뎁뎂뎃뎄뎅뎆뎇뎈뎉뎊뎋뎌뎍뎎뎏뎐뎑뎒뎓뎔뎕뎖뎗뎘뎙뎚뎛뎜뎝뎞뎟 +뎠뎡뎢뎣뎤뎥뎦뎧뎨뎩뎪뎫뎬뎭뎮뎯뎰뎱뎲뎳뎴뎵뎶뎷뎸뎹뎺뎻뎼뎽뎾뎿 +돀돁돂돃도독돆돇돈돉돊돋돌돍돎돏돐돑돒돓돔돕돖돗돘동돚돛돜돝돞돟 +돠돡돢돣돤돥돦돧돨돩돪돫돬돭돮돯돰돱돲돳돴돵돶돷돸돹돺돻돼돽돾돿 +됀됁됂됃됄됅됆됇됈됉됊됋됌됍됎됏됐됑됒됓됔됕됖됗되됙됚됛된됝됞됟 +될됡됢됣됤됥됦됧됨됩됪됫됬됭됮됯됰됱됲됳됴됵됶됷됸됹됺됻됼됽됾됿 +둀둁둂둃둄둅둆둇둈둉둊둋둌둍둎둏두둑둒둓둔둕둖둗둘둙둚둛둜둝둞둟 +둠둡둢둣둤둥둦둧둨둩둪둫둬둭둮둯둰둱둲둳둴둵둶둷둸둹둺둻둼둽둾둿 +뒀뒁뒂뒃뒄뒅뒆뒇뒈뒉뒊뒋뒌뒍뒎뒏뒐뒑뒒뒓뒔뒕뒖뒗뒘뒙뒚뒛뒜뒝뒞뒟 +뒠뒡뒢뒣뒤뒥뒦뒧뒨뒩뒪뒫뒬뒭뒮뒯뒰뒱뒲뒳뒴뒵뒶뒷뒸뒹뒺뒻뒼뒽뒾뒿 +듀듁듂듃듄듅듆듇듈듉듊듋듌듍듎듏듐듑듒듓듔듕듖듗듘듙듚듛드득듞듟 +든듡듢듣들듥듦듧듨듩듪듫듬듭듮듯듰등듲듳듴듵듶듷듸듹듺듻듼듽듾듿 +딀딁딂딃딄딅딆딇딈딉딊딋딌딍딎딏딐딑딒딓디딕딖딗딘딙딚딛딜딝딞딟 +딠딡딢딣딤딥딦딧딨딩딪딫딬딭딮딯따딱딲딳딴딵딶딷딸딹딺딻딼딽딾딿 +땀땁땂땃땄땅땆땇땈땉땊땋때땍땎땏땐땑땒땓땔땕땖땗땘땙땚땛땜땝땞땟 +땠땡땢땣땤땥땦땧땨땩땪땫땬땭땮땯땰땱땲땳땴땵땶땷땸땹땺땻땼땽땾땿 +떀떁떂떃떄떅떆떇떈떉떊떋떌떍떎떏떐떑떒떓떔떕떖떗떘떙떚떛떜떝떞떟 +떠떡떢떣떤떥떦떧떨떩떪떫떬떭떮떯떰떱떲떳떴떵떶떷떸떹떺떻떼떽떾떿 +뗀뗁뗂뗃뗄뗅뗆뗇뗈뗉뗊뗋뗌뗍뗎뗏뗐뗑뗒뗓뗔뗕뗖뗗뗘뗙뗚뗛뗜뗝뗞뗟 +뗠뗡뗢뗣뗤뗥뗦뗧뗨뗩뗪뗫뗬뗭뗮뗯뗰뗱뗲뗳뗴뗵뗶뗷뗸뗹뗺뗻뗼뗽뗾뗿 +똀똁똂똃똄똅똆똇똈똉똊똋똌똍똎똏또똑똒똓똔똕똖똗똘똙똚똛똜똝똞똟 +똠똡똢똣똤똥똦똧똨똩똪똫똬똭똮똯똰똱똲똳똴똵똶똷똸똹똺똻똼똽똾똿 +뙀뙁뙂뙃뙄뙅뙆뙇뙈뙉뙊뙋뙌뙍뙎뙏뙐뙑뙒뙓뙔뙕뙖뙗뙘뙙뙚뙛뙜뙝뙞뙟 +뙠뙡뙢뙣뙤뙥뙦뙧뙨뙩뙪뙫뙬뙭뙮뙯뙰뙱뙲뙳뙴뙵뙶뙷뙸뙹뙺뙻뙼뙽뙾뙿 +뚀뚁뚂뚃뚄뚅뚆뚇뚈뚉뚊뚋뚌뚍뚎뚏뚐뚑뚒뚓뚔뚕뚖뚗뚘뚙뚚뚛뚜뚝뚞뚟 +뚠뚡뚢뚣뚤뚥뚦뚧뚨뚩뚪뚫뚬뚭뚮뚯뚰뚱뚲뚳뚴뚵뚶뚷뚸뚹뚺뚻뚼뚽뚾뚿 +뛀뛁뛂뛃뛄뛅뛆뛇뛈뛉뛊뛋뛌뛍뛎뛏뛐뛑뛒뛓뛔뛕뛖뛗뛘뛙뛚뛛뛜뛝뛞뛟 +뛠뛡뛢뛣뛤뛥뛦뛧뛨뛩뛪뛫뛬뛭뛮뛯뛰뛱뛲뛳뛴뛵뛶뛷뛸뛹뛺뛻뛼뛽뛾뛿 +뜀뜁뜂뜃뜄뜅뜆뜇뜈뜉뜊뜋뜌뜍뜎뜏뜐뜑뜒뜓뜔뜕뜖뜗뜘뜙뜚뜛뜜뜝뜞뜟 +뜠뜡뜢뜣뜤뜥뜦뜧뜨뜩뜪뜫뜬뜭뜮뜯뜰뜱뜲뜳뜴뜵뜶뜷뜸뜹뜺뜻뜼뜽뜾뜿 +띀띁띂띃띄띅띆띇띈띉띊띋띌띍띎띏띐띑띒띓띔띕띖띗띘띙띚띛띜띝띞띟 +띠띡띢띣띤띥띦띧띨띩띪띫띬띭띮띯띰띱띲띳띴띵띶띷띸띹띺띻라락띾띿 +란랁랂랃랄랅랆랇랈랉랊랋람랍랎랏랐랑랒랓랔랕랖랗래랙랚랛랜랝랞랟 +랠랡랢랣랤랥랦랧램랩랪랫랬랭랮랯랰랱랲랳랴략랶랷랸랹랺랻랼랽랾랿 +럀럁럂럃럄럅럆럇럈량럊럋럌럍럎럏럐럑럒럓럔럕럖럗럘럙럚럛럜럝럞럟 +럠럡럢럣럤럥럦럧럨럩럪럫러럭럮럯런럱럲럳럴럵럶럷럸럹럺럻럼럽럾럿 +렀렁렂렃렄렅렆렇레렉렊렋렌렍렎렏렐렑렒렓렔렕렖렗렘렙렚렛렜렝렞렟 +렠렡렢렣려력렦렧련렩렪렫렬렭렮렯렰렱렲렳렴렵렶렷렸령렺렻렼렽렾렿 +례롁롂롃롄롅롆롇롈롉롊롋롌롍롎롏롐롑롒롓롔롕롖롗롘롙롚롛로록롞롟 +론롡롢롣롤롥롦롧롨롩롪롫롬롭롮롯롰롱롲롳롴롵롶롷롸롹롺롻롼롽롾롿 +뢀뢁뢂뢃뢄뢅뢆뢇뢈뢉뢊뢋뢌뢍뢎뢏뢐뢑뢒뢓뢔뢕뢖뢗뢘뢙뢚뢛뢜뢝뢞뢟 +뢠뢡뢢뢣뢤뢥뢦뢧뢨뢩뢪뢫뢬뢭뢮뢯뢰뢱뢲뢳뢴뢵뢶뢷뢸뢹뢺뢻뢼뢽뢾뢿 +룀룁룂룃룄룅룆룇룈룉룊룋료룍룎룏룐룑룒룓룔룕룖룗룘룙룚룛룜룝룞룟 +룠룡룢룣룤룥룦룧루룩룪룫룬룭룮룯룰룱룲룳룴룵룶룷룸룹룺룻룼룽룾룿 +뤀뤁뤂뤃뤄뤅뤆뤇뤈뤉뤊뤋뤌뤍뤎뤏뤐뤑뤒뤓뤔뤕뤖뤗뤘뤙뤚뤛뤜뤝뤞뤟 +뤠뤡뤢뤣뤤뤥뤦뤧뤨뤩뤪뤫뤬뤭뤮뤯뤰뤱뤲뤳뤴뤵뤶뤷뤸뤹뤺뤻뤼뤽뤾뤿 +륀륁륂륃륄륅륆륇륈륉륊륋륌륍륎륏륐륑륒륓륔륕륖륗류륙륚륛륜륝륞륟 +률륡륢륣륤륥륦륧륨륩륪륫륬륭륮륯륰륱륲륳르륵륶륷른륹륺륻를륽륾륿 +릀릁릂릃름릅릆릇릈릉릊릋릌릍릎릏릐릑릒릓릔릕릖릗릘릙릚릛릜릝릞릟 +릠릡릢릣릤릥릦릧릨릩릪릫리릭릮릯린릱릲릳릴릵릶릷릸릹릺릻림립릾릿 +맀링맂맃맄맅맆맇마막맊맋만맍많맏말맑맒맓맔맕맖맗맘맙맚맛맜망맞맟 +맠맡맢맣매맥맦맧맨맩맪맫맬맭맮맯맰맱맲맳맴맵맶맷맸맹맺맻맼맽맾맿 +먀먁먂먃먄먅먆먇먈먉먊먋먌먍먎먏먐먑먒먓먔먕먖먗먘먙먚먛먜먝먞먟 +먠먡먢먣먤먥먦먧먨먩먪먫먬먭먮먯먰먱먲먳먴먵먶먷머먹먺먻먼먽먾먿 +멀멁멂멃멄멅멆멇멈멉멊멋멌멍멎멏멐멑멒멓메멕멖멗멘멙멚멛멜멝멞멟 +멠멡멢멣멤멥멦멧멨멩멪멫멬멭멮멯며멱멲멳면멵멶멷멸멹멺멻멼멽멾멿 +몀몁몂몃몄명몆몇몈몉몊몋몌몍몎몏몐몑몒몓몔몕몖몗몘몙몚몛몜몝몞몟 +몠몡몢몣몤몥몦몧모목몪몫몬몭몮몯몰몱몲몳몴몵몶몷몸몹몺못몼몽몾몿 +뫀뫁뫂뫃뫄뫅뫆뫇뫈뫉뫊뫋뫌뫍뫎뫏뫐뫑뫒뫓뫔뫕뫖뫗뫘뫙뫚뫛뫜뫝뫞뫟 +뫠뫡뫢뫣뫤뫥뫦뫧뫨뫩뫪뫫뫬뫭뫮뫯뫰뫱뫲뫳뫴뫵뫶뫷뫸뫹뫺뫻뫼뫽뫾뫿 +묀묁묂묃묄묅묆묇묈묉묊묋묌묍묎묏묐묑묒묓묔묕묖묗묘묙묚묛묜묝묞묟 +묠묡묢묣묤묥묦묧묨묩묪묫묬묭묮묯묰묱묲묳무묵묶묷문묹묺묻물묽묾묿 +뭀뭁뭂뭃뭄뭅뭆뭇뭈뭉뭊뭋뭌뭍뭎뭏뭐뭑뭒뭓뭔뭕뭖뭗뭘뭙뭚뭛뭜뭝뭞뭟 +뭠뭡뭢뭣뭤뭥뭦뭧뭨뭩뭪뭫뭬뭭뭮뭯뭰뭱뭲뭳뭴뭵뭶뭷뭸뭹뭺뭻뭼뭽뭾뭿 +뮀뮁뮂뮃뮄뮅뮆뮇뮈뮉뮊뮋뮌뮍뮎뮏뮐뮑뮒뮓뮔뮕뮖뮗뮘뮙뮚뮛뮜뮝뮞뮟 +뮠뮡뮢뮣뮤뮥뮦뮧뮨뮩뮪뮫뮬뮭뮮뮯뮰뮱뮲뮳뮴뮵뮶뮷뮸뮹뮺뮻뮼뮽뮾뮿 +므믁믂믃믄믅믆믇믈믉믊믋믌믍믎믏믐믑믒믓믔믕믖믗믘믙믚믛믜믝믞믟 +믠믡믢믣믤믥믦믧믨믩믪믫믬믭믮믯믰믱믲믳믴믵믶믷미믹믺믻민믽믾믿 +밀밁밂밃밄밅밆밇밈밉밊밋밌밍밎및밐밑밒밓바박밖밗반밙밚받발밝밞밟 +밠밡밢밣밤밥밦밧밨방밪밫밬밭밮밯배백밲밳밴밵밶밷밸밹밺밻밼밽밾밿 +뱀뱁뱂뱃뱄뱅뱆뱇뱈뱉뱊뱋뱌뱍뱎뱏뱐뱑뱒뱓뱔뱕뱖뱗뱘뱙뱚뱛뱜뱝뱞뱟 +뱠뱡뱢뱣뱤뱥뱦뱧뱨뱩뱪뱫뱬뱭뱮뱯뱰뱱뱲뱳뱴뱵뱶뱷뱸뱹뱺뱻뱼뱽뱾뱿 +벀벁벂벃버벅벆벇번벉벊벋벌벍벎벏벐벑벒벓범법벖벗벘벙벚벛벜벝벞벟 +베벡벢벣벤벥벦벧벨벩벪벫벬벭벮벯벰벱벲벳벴벵벶벷벸벹벺벻벼벽벾벿 +변볁볂볃별볅볆볇볈볉볊볋볌볍볎볏볐병볒볓볔볕볖볗볘볙볚볛볜볝볞볟 +볠볡볢볣볤볥볦볧볨볩볪볫볬볭볮볯볰볱볲볳보복볶볷본볹볺볻볼볽볾볿 +봀봁봂봃봄봅봆봇봈봉봊봋봌봍봎봏봐봑봒봓봔봕봖봗봘봙봚봛봜봝봞봟 +봠봡봢봣봤봥봦봧봨봩봪봫봬봭봮봯봰봱봲봳봴봵봶봷봸봹봺봻봼봽봾봿 +뵀뵁뵂뵃뵄뵅뵆뵇뵈뵉뵊뵋뵌뵍뵎뵏뵐뵑뵒뵓뵔뵕뵖뵗뵘뵙뵚뵛뵜뵝뵞뵟 +뵠뵡뵢뵣뵤뵥뵦뵧뵨뵩뵪뵫뵬뵭뵮뵯뵰뵱뵲뵳뵴뵵뵶뵷뵸뵹뵺뵻뵼뵽뵾뵿 +부북붂붃분붅붆붇불붉붊붋붌붍붎붏붐붑붒붓붔붕붖붗붘붙붚붛붜붝붞붟 +붠붡붢붣붤붥붦붧붨붩붪붫붬붭붮붯붰붱붲붳붴붵붶붷붸붹붺붻붼붽붾붿 +뷀뷁뷂뷃뷄뷅뷆뷇뷈뷉뷊뷋뷌뷍뷎뷏뷐뷑뷒뷓뷔뷕뷖뷗뷘뷙뷚뷛뷜뷝뷞뷟 +뷠뷡뷢뷣뷤뷥뷦뷧뷨뷩뷪뷫뷬뷭뷮뷯뷰뷱뷲뷳뷴뷵뷶뷷뷸뷹뷺뷻뷼뷽뷾뷿 +븀븁븂븃븄븅븆븇븈븉븊븋브븍븎븏븐븑븒븓블븕븖븗븘븙븚븛븜븝븞븟 +븠븡븢븣븤븥븦븧븨븩븪븫븬븭븮븯븰븱븲븳븴븵븶븷븸븹븺븻븼븽븾븿 +빀빁빂빃비빅빆빇빈빉빊빋빌빍빎빏빐빑빒빓빔빕빖빗빘빙빚빛빜빝빞빟 +빠빡빢빣빤빥빦빧빨빩빪빫빬빭빮빯빰빱빲빳빴빵빶빷빸빹빺빻빼빽빾빿 +뺀뺁뺂뺃뺄뺅뺆뺇뺈뺉뺊뺋뺌뺍뺎뺏뺐뺑뺒뺓뺔뺕뺖뺗뺘뺙뺚뺛뺜뺝뺞뺟 +뺠뺡뺢뺣뺤뺥뺦뺧뺨뺩뺪뺫뺬뺭뺮뺯뺰뺱뺲뺳뺴뺵뺶뺷뺸뺹뺺뺻뺼뺽뺾뺿 +뻀뻁뻂뻃뻄뻅뻆뻇뻈뻉뻊뻋뻌뻍뻎뻏뻐뻑뻒뻓뻔뻕뻖뻗뻘뻙뻚뻛뻜뻝뻞뻟 +뻠뻡뻢뻣뻤뻥뻦뻧뻨뻩뻪뻫뻬뻭뻮뻯뻰뻱뻲뻳뻴뻵뻶뻷뻸뻹뻺뻻뻼뻽뻾뻿 +뼀뼁뼂뼃뼄뼅뼆뼇뼈뼉뼊뼋뼌뼍뼎뼏뼐뼑뼒뼓뼔뼕뼖뼗뼘뼙뼚뼛뼜뼝뼞뼟 +뼠뼡뼢뼣뼤뼥뼦뼧뼨뼩뼪뼫뼬뼭뼮뼯뼰뼱뼲뼳뼴뼵뼶뼷뼸뼹뼺뼻뼼뼽뼾뼿 +뽀뽁뽂뽃뽄뽅뽆뽇뽈뽉뽊뽋뽌뽍뽎뽏뽐뽑뽒뽓뽔뽕뽖뽗뽘뽙뽚뽛뽜뽝뽞뽟 +뽠뽡뽢뽣뽤뽥뽦뽧뽨뽩뽪뽫뽬뽭뽮뽯뽰뽱뽲뽳뽴뽵뽶뽷뽸뽹뽺뽻뽼뽽뽾뽿 +뾀뾁뾂뾃뾄뾅뾆뾇뾈뾉뾊뾋뾌뾍뾎뾏뾐뾑뾒뾓뾔뾕뾖뾗뾘뾙뾚뾛뾜뾝뾞뾟 +뾠뾡뾢뾣뾤뾥뾦뾧뾨뾩뾪뾫뾬뾭뾮뾯뾰뾱뾲뾳뾴뾵뾶뾷뾸뾹뾺뾻뾼뾽뾾뾿 +뿀뿁뿂뿃뿄뿅뿆뿇뿈뿉뿊뿋뿌뿍뿎뿏뿐뿑뿒뿓뿔뿕뿖뿗뿘뿙뿚뿛뿜뿝뿞뿟 +뿠뿡뿢뿣뿤뿥뿦뿧뿨뿩뿪뿫뿬뿭뿮뿯뿰뿱뿲뿳뿴뿵뿶뿷뿸뿹뿺뿻뿼뿽뿾뿿 +쀀쀁쀂쀃쀄쀅쀆쀇쀈쀉쀊쀋쀌쀍쀎쀏쀐쀑쀒쀓쀔쀕쀖쀗쀘쀙쀚쀛쀜쀝쀞쀟 +쀠쀡쀢쀣쀤쀥쀦쀧쀨쀩쀪쀫쀬쀭쀮쀯쀰쀱쀲쀳쀴쀵쀶쀷쀸쀹쀺쀻쀼쀽쀾쀿 +쁀쁁쁂쁃쁄쁅쁆쁇쁈쁉쁊쁋쁌쁍쁎쁏쁐쁑쁒쁓쁔쁕쁖쁗쁘쁙쁚쁛쁜쁝쁞쁟 +쁠쁡쁢쁣쁤쁥쁦쁧쁨쁩쁪쁫쁬쁭쁮쁯쁰쁱쁲쁳쁴쁵쁶쁷쁸쁹쁺쁻쁼쁽쁾쁿 +삀삁삂삃삄삅삆삇삈삉삊삋삌삍삎삏삐삑삒삓삔삕삖삗삘삙삚삛삜삝삞삟 +삠삡삢삣삤삥삦삧삨삩삪삫사삭삮삯산삱삲삳살삵삶삷삸삹삺삻삼삽삾삿 +샀상샂샃샄샅샆샇새색샊샋샌샍샎샏샐샑샒샓샔샕샖샗샘샙샚샛샜생샞샟 +샠샡샢샣샤샥샦샧샨샩샪샫샬샭샮샯샰샱샲샳샴샵샶샷샸샹샺샻샼샽샾샿 +섀섁섂섃섄섅섆섇섈섉섊섋섌섍섎섏섐섑섒섓섔섕섖섗섘섙섚섛서석섞섟 +선섡섢섣설섥섦섧섨섩섪섫섬섭섮섯섰성섲섳섴섵섶섷세섹섺섻센섽섾섿 +셀셁셂셃셄셅셆셇셈셉셊셋셌셍셎셏셐셑셒셓셔셕셖셗션셙셚셛셜셝셞셟 +셠셡셢셣셤셥셦셧셨셩셪셫셬셭셮셯셰셱셲셳셴셵셶셷셸셹셺셻셼셽셾셿 +솀솁솂솃솄솅솆솇솈솉솊솋소속솎솏손솑솒솓솔솕솖솗솘솙솚솛솜솝솞솟 +솠송솢솣솤솥솦솧솨솩솪솫솬솭솮솯솰솱솲솳솴솵솶솷솸솹솺솻솼솽솾솿 +쇀쇁쇂쇃쇄쇅쇆쇇쇈쇉쇊쇋쇌쇍쇎쇏쇐쇑쇒쇓쇔쇕쇖쇗쇘쇙쇚쇛쇜쇝쇞쇟 +쇠쇡쇢쇣쇤쇥쇦쇧쇨쇩쇪쇫쇬쇭쇮쇯쇰쇱쇲쇳쇴쇵쇶쇷쇸쇹쇺쇻쇼쇽쇾쇿 +숀숁숂숃숄숅숆숇숈숉숊숋숌숍숎숏숐숑숒숓숔숕숖숗수숙숚숛순숝숞숟 +술숡숢숣숤숥숦숧숨숩숪숫숬숭숮숯숰숱숲숳숴숵숶숷숸숹숺숻숼숽숾숿 +쉀쉁쉂쉃쉄쉅쉆쉇쉈쉉쉊쉋쉌쉍쉎쉏쉐쉑쉒쉓쉔쉕쉖쉗쉘쉙쉚쉛쉜쉝쉞쉟 +쉠쉡쉢쉣쉤쉥쉦쉧쉨쉩쉪쉫쉬쉭쉮쉯쉰쉱쉲쉳쉴쉵쉶쉷쉸쉹쉺쉻쉼쉽쉾쉿 +슀슁슂슃슄슅슆슇슈슉슊슋슌슍슎슏슐슑슒슓슔슕슖슗슘슙슚슛슜슝슞슟 +슠슡슢슣스슥슦슧슨슩슪슫슬슭슮슯슰슱슲슳슴습슶슷슸승슺슻슼슽슾슿 +싀싁싂싃싄싅싆싇싈싉싊싋싌싍싎싏싐싑싒싓싔싕싖싗싘싙싚싛시식싞싟 +신싡싢싣실싥싦싧싨싩싪싫심십싮싯싰싱싲싳싴싵싶싷싸싹싺싻싼싽싾싿 +쌀쌁쌂쌃쌄쌅쌆쌇쌈쌉쌊쌋쌌쌍쌎쌏쌐쌑쌒쌓쌔쌕쌖쌗쌘쌙쌚쌛쌜쌝쌞쌟 +쌠쌡쌢쌣쌤쌥쌦쌧쌨쌩쌪쌫쌬쌭쌮쌯쌰쌱쌲쌳쌴쌵쌶쌷쌸쌹쌺쌻쌼쌽쌾쌿 +썀썁썂썃썄썅썆썇썈썉썊썋썌썍썎썏썐썑썒썓썔썕썖썗썘썙썚썛썜썝썞썟 +썠썡썢썣썤썥썦썧써썩썪썫썬썭썮썯썰썱썲썳썴썵썶썷썸썹썺썻썼썽썾썿 +쎀쎁쎂쎃쎄쎅쎆쎇쎈쎉쎊쎋쎌쎍쎎쎏쎐쎑쎒쎓쎔쎕쎖쎗쎘쎙쎚쎛쎜쎝쎞쎟 +쎠쎡쎢쎣쎤쎥쎦쎧쎨쎩쎪쎫쎬쎭쎮쎯쎰쎱쎲쎳쎴쎵쎶쎷쎸쎹쎺쎻쎼쎽쎾쎿 +쏀쏁쏂쏃쏄쏅쏆쏇쏈쏉쏊쏋쏌쏍쏎쏏쏐쏑쏒쏓쏔쏕쏖쏗쏘쏙쏚쏛쏜쏝쏞쏟 +쏠쏡쏢쏣쏤쏥쏦쏧쏨쏩쏪쏫쏬쏭쏮쏯쏰쏱쏲쏳쏴쏵쏶쏷쏸쏹쏺쏻쏼쏽쏾쏿 +쐀쐁쐂쐃쐄쐅쐆쐇쐈쐉쐊쐋쐌쐍쐎쐏쐐쐑쐒쐓쐔쐕쐖쐗쐘쐙쐚쐛쐜쐝쐞쐟 +쐠쐡쐢쐣쐤쐥쐦쐧쐨쐩쐪쐫쐬쐭쐮쐯쐰쐱쐲쐳쐴쐵쐶쐷쐸쐹쐺쐻쐼쐽쐾쐿 +쑀쑁쑂쑃쑄쑅쑆쑇쑈쑉쑊쑋쑌쑍쑎쑏쑐쑑쑒쑓쑔쑕쑖쑗쑘쑙쑚쑛쑜쑝쑞쑟 +쑠쑡쑢쑣쑤쑥쑦쑧쑨쑩쑪쑫쑬쑭쑮쑯쑰쑱쑲쑳쑴쑵쑶쑷쑸쑹쑺쑻쑼쑽쑾쑿 +쒀쒁쒂쒃쒄쒅쒆쒇쒈쒉쒊쒋쒌쒍쒎쒏쒐쒑쒒쒓쒔쒕쒖쒗쒘쒙쒚쒛쒜쒝쒞쒟 +쒠쒡쒢쒣쒤쒥쒦쒧쒨쒩쒪쒫쒬쒭쒮쒯쒰쒱쒲쒳쒴쒵쒶쒷쒸쒹쒺쒻쒼쒽쒾쒿 +쓀쓁쓂쓃쓄쓅쓆쓇쓈쓉쓊쓋쓌쓍쓎쓏쓐쓑쓒쓓쓔쓕쓖쓗쓘쓙쓚쓛쓜쓝쓞쓟 +쓠쓡쓢쓣쓤쓥쓦쓧쓨쓩쓪쓫쓬쓭쓮쓯쓰쓱쓲쓳쓴쓵쓶쓷쓸쓹쓺쓻쓼쓽쓾쓿 +씀씁씂씃씄씅씆씇씈씉씊씋씌씍씎씏씐씑씒씓씔씕씖씗씘씙씚씛씜씝씞씟 +씠씡씢씣씤씥씦씧씨씩씪씫씬씭씮씯씰씱씲씳씴씵씶씷씸씹씺씻씼씽씾씿 +앀앁앂앃아악앆앇안앉않앋알앍앎앏앐앑앒앓암압앖앗았앙앚앛앜앝앞앟 +애액앢앣앤앥앦앧앨앩앪앫앬앭앮앯앰앱앲앳앴앵앶앷앸앹앺앻야약앾앿 +얀얁얂얃얄얅얆얇얈얉얊얋얌얍얎얏얐양얒얓얔얕얖얗얘얙얚얛얜얝얞얟 +얠얡얢얣얤얥얦얧얨얩얪얫얬얭얮얯얰얱얲얳어억얶얷언얹얺얻얼얽얾얿 +엀엁엂엃엄업없엇었엉엊엋엌엍엎엏에엑엒엓엔엕엖엗엘엙엚엛엜엝엞엟 +엠엡엢엣엤엥엦엧엨엩엪엫여역엮엯연엱엲엳열엵엶엷엸엹엺엻염엽엾엿 +였영옂옃옄옅옆옇예옉옊옋옌옍옎옏옐옑옒옓옔옕옖옗옘옙옚옛옜옝옞옟 +옠옡옢옣오옥옦옧온옩옪옫올옭옮옯옰옱옲옳옴옵옶옷옸옹옺옻옼옽옾옿 +와왁왂왃완왅왆왇왈왉왊왋왌왍왎왏왐왑왒왓왔왕왖왗왘왙왚왛왜왝왞왟 +왠왡왢왣왤왥왦왧왨왩왪왫왬왭왮왯왰왱왲왳왴왵왶왷외왹왺왻왼왽왾왿 +욀욁욂욃욄욅욆욇욈욉욊욋욌욍욎욏욐욑욒욓요욕욖욗욘욙욚욛욜욝욞욟 +욠욡욢욣욤욥욦욧욨용욪욫욬욭욮욯우욱욲욳운욵욶욷울욹욺욻욼욽욾욿 +움웁웂웃웄웅웆웇웈웉웊웋워웍웎웏원웑웒웓월웕웖웗웘웙웚웛웜웝웞웟 +웠웡웢웣웤웥웦웧웨웩웪웫웬웭웮웯웰웱웲웳웴웵웶웷웸웹웺웻웼웽웾웿 +윀윁윂윃위윅윆윇윈윉윊윋윌윍윎윏윐윑윒윓윔윕윖윗윘윙윚윛윜윝윞윟 +유육윢윣윤윥윦윧율윩윪윫윬윭윮윯윰윱윲윳윴융윶윷윸윹윺윻으윽윾윿 +은읁읂읃을읅읆읇읈읉읊읋음읍읎읏읐응읒읓읔읕읖읗의읙읚읛읜읝읞읟 +읠읡읢읣읤읥읦읧읨읩읪읫읬읭읮읯읰읱읲읳이익읶읷인읹읺읻일읽읾읿 +잀잁잂잃임입잆잇있잉잊잋잌잍잎잏자작잒잓잔잕잖잗잘잙잚잛잜잝잞잟 +잠잡잢잣잤장잦잧잨잩잪잫재잭잮잯잰잱잲잳잴잵잶잷잸잹잺잻잼잽잾잿 +쟀쟁쟂쟃쟄쟅쟆쟇쟈쟉쟊쟋쟌쟍쟎쟏쟐쟑쟒쟓쟔쟕쟖쟗쟘쟙쟚쟛쟜쟝쟞쟟 +쟠쟡쟢쟣쟤쟥쟦쟧쟨쟩쟪쟫쟬쟭쟮쟯쟰쟱쟲쟳쟴쟵쟶쟷쟸쟹쟺쟻쟼쟽쟾쟿 +저적젂젃전젅젆젇절젉젊젋젌젍젎젏점접젒젓젔정젖젗젘젙젚젛제젝젞젟 +젠젡젢젣젤젥젦젧젨젩젪젫젬젭젮젯젰젱젲젳젴젵젶젷져젹젺젻젼젽젾젿 +졀졁졂졃졄졅졆졇졈졉졊졋졌졍졎졏졐졑졒졓졔졕졖졗졘졙졚졛졜졝졞졟 +졠졡졢졣졤졥졦졧졨졩졪졫졬졭졮졯조족졲졳존졵졶졷졸졹졺졻졼졽졾졿 +좀좁좂좃좄종좆좇좈좉좊좋좌좍좎좏좐좑좒좓좔좕좖좗좘좙좚좛좜좝좞좟 +좠좡좢좣좤좥좦좧좨좩좪좫좬좭좮좯좰좱좲좳좴좵좶좷좸좹좺좻좼좽좾좿 +죀죁죂죃죄죅죆죇죈죉죊죋죌죍죎죏죐죑죒죓죔죕죖죗죘죙죚죛죜죝죞죟 +죠죡죢죣죤죥죦죧죨죩죪죫죬죭죮죯죰죱죲죳죴죵죶죷죸죹죺죻주죽죾죿 +준줁줂줃줄줅줆줇줈줉줊줋줌줍줎줏줐중줒줓줔줕줖줗줘줙줚줛줜줝줞줟 +줠줡줢줣줤줥줦줧줨줩줪줫줬줭줮줯줰줱줲줳줴줵줶줷줸줹줺줻줼줽줾줿 +쥀쥁쥂쥃쥄쥅쥆쥇쥈쥉쥊쥋쥌쥍쥎쥏쥐쥑쥒쥓쥔쥕쥖쥗쥘쥙쥚쥛쥜쥝쥞쥟 +쥠쥡쥢쥣쥤쥥쥦쥧쥨쥩쥪쥫쥬쥭쥮쥯쥰쥱쥲쥳쥴쥵쥶쥷쥸쥹쥺쥻쥼쥽쥾쥿 +즀즁즂즃즄즅즆즇즈즉즊즋즌즍즎즏즐즑즒즓즔즕즖즗즘즙즚즛즜증즞즟 +즠즡즢즣즤즥즦즧즨즩즪즫즬즭즮즯즰즱즲즳즴즵즶즷즸즹즺즻즼즽즾즿 +지직짂짃진짅짆짇질짉짊짋짌짍짎짏짐집짒짓짔징짖짗짘짙짚짛짜짝짞짟 +짠짡짢짣짤짥짦짧짨짩짪짫짬짭짮짯짰짱짲짳짴짵짶짷째짹짺짻짼짽짾짿 +쨀쨁쨂쨃쨄쨅쨆쨇쨈쨉쨊쨋쨌쨍쨎쨏쨐쨑쨒쨓쨔쨕쨖쨗쨘쨙쨚쨛쨜쨝쨞쨟 +쨠쨡쨢쨣쨤쨥쨦쨧쨨쨩쨪쨫쨬쨭쨮쨯쨰쨱쨲쨳쨴쨵쨶쨷쨸쨹쨺쨻쨼쨽쨾쨿 +쩀쩁쩂쩃쩄쩅쩆쩇쩈쩉쩊쩋쩌쩍쩎쩏쩐쩑쩒쩓쩔쩕쩖쩗쩘쩙쩚쩛쩜쩝쩞쩟 +쩠쩡쩢쩣쩤쩥쩦쩧쩨쩩쩪쩫쩬쩭쩮쩯쩰쩱쩲쩳쩴쩵쩶쩷쩸쩹쩺쩻쩼쩽쩾쩿 +쪀쪁쪂쪃쪄쪅쪆쪇쪈쪉쪊쪋쪌쪍쪎쪏쪐쪑쪒쪓쪔쪕쪖쪗쪘쪙쪚쪛쪜쪝쪞쪟 +쪠쪡쪢쪣쪤쪥쪦쪧쪨쪩쪪쪫쪬쪭쪮쪯쪰쪱쪲쪳쪴쪵쪶쪷쪸쪹쪺쪻쪼쪽쪾쪿 +쫀쫁쫂쫃쫄쫅쫆쫇쫈쫉쫊쫋쫌쫍쫎쫏쫐쫑쫒쫓쫔쫕쫖쫗쫘쫙쫚쫛쫜쫝쫞쫟 +쫠쫡쫢쫣쫤쫥쫦쫧쫨쫩쫪쫫쫬쫭쫮쫯쫰쫱쫲쫳쫴쫵쫶쫷쫸쫹쫺쫻쫼쫽쫾쫿 +쬀쬁쬂쬃쬄쬅쬆쬇쬈쬉쬊쬋쬌쬍쬎쬏쬐쬑쬒쬓쬔쬕쬖쬗쬘쬙쬚쬛쬜쬝쬞쬟 +쬠쬡쬢쬣쬤쬥쬦쬧쬨쬩쬪쬫쬬쬭쬮쬯쬰쬱쬲쬳쬴쬵쬶쬷쬸쬹쬺쬻쬼쬽쬾쬿 +쭀쭁쭂쭃쭄쭅쭆쭇쭈쭉쭊쭋쭌쭍쭎쭏쭐쭑쭒쭓쭔쭕쭖쭗쭘쭙쭚쭛쭜쭝쭞쭟 +쭠쭡쭢쭣쭤쭥쭦쭧쭨쭩쭪쭫쭬쭭쭮쭯쭰쭱쭲쭳쭴쭵쭶쭷쭸쭹쭺쭻쭼쭽쭾쭿 +쮀쮁쮂쮃쮄쮅쮆쮇쮈쮉쮊쮋쮌쮍쮎쮏쮐쮑쮒쮓쮔쮕쮖쮗쮘쮙쮚쮛쮜쮝쮞쮟 +쮠쮡쮢쮣쮤쮥쮦쮧쮨쮩쮪쮫쮬쮭쮮쮯쮰쮱쮲쮳쮴쮵쮶쮷쮸쮹쮺쮻쮼쮽쮾쮿 +쯀쯁쯂쯃쯄쯅쯆쯇쯈쯉쯊쯋쯌쯍쯎쯏쯐쯑쯒쯓쯔쯕쯖쯗쯘쯙쯚쯛쯜쯝쯞쯟 +쯠쯡쯢쯣쯤쯥쯦쯧쯨쯩쯪쯫쯬쯭쯮쯯쯰쯱쯲쯳쯴쯵쯶쯷쯸쯹쯺쯻쯼쯽쯾쯿 +찀찁찂찃찄찅찆찇찈찉찊찋찌찍찎찏찐찑찒찓찔찕찖찗찘찙찚찛찜찝찞찟 +찠찡찢찣찤찥찦찧차착찪찫찬찭찮찯찰찱찲찳찴찵찶찷참찹찺찻찼창찾찿 +챀챁챂챃채책챆챇챈챉챊챋챌챍챎챏챐챑챒챓챔챕챖챗챘챙챚챛챜챝챞챟 +챠챡챢챣챤챥챦챧챨챩챪챫챬챭챮챯챰챱챲챳챴챵챶챷챸챹챺챻챼챽챾챿 +첀첁첂첃첄첅첆첇첈첉첊첋첌첍첎첏첐첑첒첓첔첕첖첗처척첚첛천첝첞첟 +철첡첢첣첤첥첦첧첨첩첪첫첬청첮첯첰첱첲첳체첵첶첷첸첹첺첻첼첽첾첿 +쳀쳁쳂쳃쳄쳅쳆쳇쳈쳉쳊쳋쳌쳍쳎쳏쳐쳑쳒쳓쳔쳕쳖쳗쳘쳙쳚쳛쳜쳝쳞쳟 +쳠쳡쳢쳣쳤쳥쳦쳧쳨쳩쳪쳫쳬쳭쳮쳯쳰쳱쳲쳳쳴쳵쳶쳷쳸쳹쳺쳻쳼쳽쳾쳿 +촀촁촂촃촄촅촆촇초촉촊촋촌촍촎촏촐촑촒촓촔촕촖촗촘촙촚촛촜총촞촟 +촠촡촢촣촤촥촦촧촨촩촪촫촬촭촮촯촰촱촲촳촴촵촶촷촸촹촺촻촼촽촾촿 +쵀쵁쵂쵃쵄쵅쵆쵇쵈쵉쵊쵋쵌쵍쵎쵏쵐쵑쵒쵓쵔쵕쵖쵗쵘쵙쵚쵛최쵝쵞쵟 +쵠쵡쵢쵣쵤쵥쵦쵧쵨쵩쵪쵫쵬쵭쵮쵯쵰쵱쵲쵳쵴쵵쵶쵷쵸쵹쵺쵻쵼쵽쵾쵿 +춀춁춂춃춄춅춆춇춈춉춊춋춌춍춎춏춐춑춒춓추축춖춗춘춙춚춛출춝춞춟 +춠춡춢춣춤춥춦춧춨충춪춫춬춭춮춯춰춱춲춳춴춵춶춷춸춹춺춻춼춽춾춿 +췀췁췂췃췄췅췆췇췈췉췊췋췌췍췎췏췐췑췒췓췔췕췖췗췘췙췚췛췜췝췞췟 +췠췡췢췣췤췥췦췧취췩췪췫췬췭췮췯췰췱췲췳췴췵췶췷췸췹췺췻췼췽췾췿 +츀츁츂츃츄츅츆츇츈츉츊츋츌츍츎츏츐츑츒츓츔츕츖츗츘츙츚츛츜츝츞츟 +츠측츢츣츤츥츦츧츨츩츪츫츬츭츮츯츰츱츲츳츴층츶츷츸츹츺츻츼츽츾츿 +칀칁칂칃칄칅칆칇칈칉칊칋칌칍칎칏칐칑칒칓칔칕칖칗치칙칚칛친칝칞칟 +칠칡칢칣칤칥칦칧침칩칪칫칬칭칮칯칰칱칲칳카칵칶칷칸칹칺칻칼칽칾칿 +캀캁캂캃캄캅캆캇캈캉캊캋캌캍캎캏캐캑캒캓캔캕캖캗캘캙캚캛캜캝캞캟 +캠캡캢캣캤캥캦캧캨캩캪캫캬캭캮캯캰캱캲캳캴캵캶캷캸캹캺캻캼캽캾캿 +컀컁컂컃컄컅컆컇컈컉컊컋컌컍컎컏컐컑컒컓컔컕컖컗컘컙컚컛컜컝컞컟 +컠컡컢컣커컥컦컧컨컩컪컫컬컭컮컯컰컱컲컳컴컵컶컷컸컹컺컻컼컽컾컿 +케켁켂켃켄켅켆켇켈켉켊켋켌켍켎켏켐켑켒켓켔켕켖켗켘켙켚켛켜켝켞켟 +켠켡켢켣켤켥켦켧켨켩켪켫켬켭켮켯켰켱켲켳켴켵켶켷켸켹켺켻켼켽켾켿 +콀콁콂콃콄콅콆콇콈콉콊콋콌콍콎콏콐콑콒콓코콕콖콗콘콙콚콛콜콝콞콟 +콠콡콢콣콤콥콦콧콨콩콪콫콬콭콮콯콰콱콲콳콴콵콶콷콸콹콺콻콼콽콾콿 +쾀쾁쾂쾃쾄쾅쾆쾇쾈쾉쾊쾋쾌쾍쾎쾏쾐쾑쾒쾓쾔쾕쾖쾗쾘쾙쾚쾛쾜쾝쾞쾟 +쾠쾡쾢쾣쾤쾥쾦쾧쾨쾩쾪쾫쾬쾭쾮쾯쾰쾱쾲쾳쾴쾵쾶쾷쾸쾹쾺쾻쾼쾽쾾쾿 +쿀쿁쿂쿃쿄쿅쿆쿇쿈쿉쿊쿋쿌쿍쿎쿏쿐쿑쿒쿓쿔쿕쿖쿗쿘쿙쿚쿛쿜쿝쿞쿟 +쿠쿡쿢쿣쿤쿥쿦쿧쿨쿩쿪쿫쿬쿭쿮쿯쿰쿱쿲쿳쿴쿵쿶쿷쿸쿹쿺쿻쿼쿽쿾쿿 +퀀퀁퀂퀃퀄퀅퀆퀇퀈퀉퀊퀋퀌퀍퀎퀏퀐퀑퀒퀓퀔퀕퀖퀗퀘퀙퀚퀛퀜퀝퀞퀟 +퀠퀡퀢퀣퀤퀥퀦퀧퀨퀩퀪퀫퀬퀭퀮퀯퀰퀱퀲퀳퀴퀵퀶퀷퀸퀹퀺퀻퀼퀽퀾퀿 +큀큁큂큃큄큅큆큇큈큉큊큋큌큍큎큏큐큑큒큓큔큕큖큗큘큙큚큛큜큝큞큟 +큠큡큢큣큤큥큦큧큨큩큪큫크큭큮큯큰큱큲큳클큵큶큷큸큹큺큻큼큽큾큿 +킀킁킂킃킄킅킆킇킈킉킊킋킌킍킎킏킐킑킒킓킔킕킖킗킘킙킚킛킜킝킞킟 +킠킡킢킣키킥킦킧킨킩킪킫킬킭킮킯킰킱킲킳킴킵킶킷킸킹킺킻킼킽킾킿 +타탁탂탃탄탅탆탇탈탉탊탋탌탍탎탏탐탑탒탓탔탕탖탗탘탙탚탛태택탞탟 +탠탡탢탣탤탥탦탧탨탩탪탫탬탭탮탯탰탱탲탳탴탵탶탷탸탹탺탻탼탽탾탿 +턀턁턂턃턄턅턆턇턈턉턊턋턌턍턎턏턐턑턒턓턔턕턖턗턘턙턚턛턜턝턞턟 +턠턡턢턣턤턥턦턧턨턩턪턫턬턭턮턯터턱턲턳턴턵턶턷털턹턺턻턼턽턾턿 +텀텁텂텃텄텅텆텇텈텉텊텋테텍텎텏텐텑텒텓텔텕텖텗텘텙텚텛템텝텞텟 +텠텡텢텣텤텥텦텧텨텩텪텫텬텭텮텯텰텱텲텳텴텵텶텷텸텹텺텻텼텽텾텿 +톀톁톂톃톄톅톆톇톈톉톊톋톌톍톎톏톐톑톒톓톔톕톖톗톘톙톚톛톜톝톞톟 +토톡톢톣톤톥톦톧톨톩톪톫톬톭톮톯톰톱톲톳톴통톶톷톸톹톺톻톼톽톾톿 +퇀퇁퇂퇃퇄퇅퇆퇇퇈퇉퇊퇋퇌퇍퇎퇏퇐퇑퇒퇓퇔퇕퇖퇗퇘퇙퇚퇛퇜퇝퇞퇟 +퇠퇡퇢퇣퇤퇥퇦퇧퇨퇩퇪퇫퇬퇭퇮퇯퇰퇱퇲퇳퇴퇵퇶퇷퇸퇹퇺퇻퇼퇽퇾퇿 +툀툁툂툃툄툅툆툇툈툉툊툋툌툍툎툏툐툑툒툓툔툕툖툗툘툙툚툛툜툝툞툟 +툠툡툢툣툤툥툦툧툨툩툪툫투툭툮툯툰툱툲툳툴툵툶툷툸툹툺툻툼툽툾툿 +퉀퉁퉂퉃퉄퉅퉆퉇퉈퉉퉊퉋퉌퉍퉎퉏퉐퉑퉒퉓퉔퉕퉖퉗퉘퉙퉚퉛퉜퉝퉞퉟 +퉠퉡퉢퉣퉤퉥퉦퉧퉨퉩퉪퉫퉬퉭퉮퉯퉰퉱퉲퉳퉴퉵퉶퉷퉸퉹퉺퉻퉼퉽퉾퉿 +튀튁튂튃튄튅튆튇튈튉튊튋튌튍튎튏튐튑튒튓튔튕튖튗튘튙튚튛튜튝튞튟 +튠튡튢튣튤튥튦튧튨튩튪튫튬튭튮튯튰튱튲튳튴튵튶튷트특튺튻튼튽튾튿 +틀틁틂틃틄틅틆틇틈틉틊틋틌틍틎틏틐틑틒틓틔틕틖틗틘틙틚틛틜틝틞틟 +틠틡틢틣틤틥틦틧틨틩틪틫틬틭틮틯티틱틲틳틴틵틶틷틸틹틺틻틼틽틾틿 +팀팁팂팃팄팅팆팇팈팉팊팋파팍팎팏판팑팒팓팔팕팖팗팘팙팚팛팜팝팞팟 +팠팡팢팣팤팥팦팧패팩팪팫팬팭팮팯팰팱팲팳팴팵팶팷팸팹팺팻팼팽팾팿 +퍀퍁퍂퍃퍄퍅퍆퍇퍈퍉퍊퍋퍌퍍퍎퍏퍐퍑퍒퍓퍔퍕퍖퍗퍘퍙퍚퍛퍜퍝퍞퍟 +퍠퍡퍢퍣퍤퍥퍦퍧퍨퍩퍪퍫퍬퍭퍮퍯퍰퍱퍲퍳퍴퍵퍶퍷퍸퍹퍺퍻퍼퍽퍾퍿 +펀펁펂펃펄펅펆펇펈펉펊펋펌펍펎펏펐펑펒펓펔펕펖펗페펙펚펛펜펝펞펟 +펠펡펢펣펤펥펦펧펨펩펪펫펬펭펮펯펰펱펲펳펴펵펶펷편펹펺펻펼펽펾펿 +폀폁폂폃폄폅폆폇폈평폊폋폌폍폎폏폐폑폒폓폔폕폖폗폘폙폚폛폜폝폞폟 +폠폡폢폣폤폥폦폧폨폩폪폫포폭폮폯폰폱폲폳폴폵폶폷폸폹폺폻폼폽폾폿 +퐀퐁퐂퐃퐄퐅퐆퐇퐈퐉퐊퐋퐌퐍퐎퐏퐐퐑퐒퐓퐔퐕퐖퐗퐘퐙퐚퐛퐜퐝퐞퐟 +퐠퐡퐢퐣퐤퐥퐦퐧퐨퐩퐪퐫퐬퐭퐮퐯퐰퐱퐲퐳퐴퐵퐶퐷퐸퐹퐺퐻퐼퐽퐾퐿 +푀푁푂푃푄푅푆푇푈푉푊푋푌푍푎푏푐푑푒푓푔푕푖푗푘푙푚푛표푝푞푟 +푠푡푢푣푤푥푦푧푨푩푪푫푬푭푮푯푰푱푲푳푴푵푶푷푸푹푺푻푼푽푾푿 +풀풁풂풃풄풅풆풇품풉풊풋풌풍풎풏풐풑풒풓풔풕풖풗풘풙풚풛풜풝풞풟 +풠풡풢풣풤풥풦풧풨풩풪풫풬풭풮풯풰풱풲풳풴풵풶풷풸풹풺풻풼풽풾풿 +퓀퓁퓂퓃퓄퓅퓆퓇퓈퓉퓊퓋퓌퓍퓎퓏퓐퓑퓒퓓퓔퓕퓖퓗퓘퓙퓚퓛퓜퓝퓞퓟 +퓠퓡퓢퓣퓤퓥퓦퓧퓨퓩퓪퓫퓬퓭퓮퓯퓰퓱퓲퓳퓴퓵퓶퓷퓸퓹퓺퓻퓼퓽퓾퓿 +픀픁픂픃프픅픆픇픈픉픊픋플픍픎픏픐픑픒픓픔픕픖픗픘픙픚픛픜픝픞픟 +픠픡픢픣픤픥픦픧픨픩픪픫픬픭픮픯픰픱픲픳픴픵픶픷픸픹픺픻피픽픾픿 +핀핁핂핃필핅핆핇핈핉핊핋핌핍핎핏핐핑핒핓핔핕핖핗하학핚핛한핝핞핟 +할핡핢핣핤핥핦핧함합핪핫핬항핮핯핰핱핲핳해핵핶핷핸핹핺핻핼핽핾핿 +햀햁햂햃햄햅햆햇했행햊햋햌햍햎햏햐햑햒햓햔햕햖햗햘햙햚햛햜햝햞햟 +햠햡햢햣햤향햦햧햨햩햪햫햬햭햮햯햰햱햲햳햴햵햶햷햸햹햺햻햼햽햾햿 +헀헁헂헃헄헅헆헇허헉헊헋헌헍헎헏헐헑헒헓헔헕헖헗험헙헚헛헜헝헞헟 +헠헡헢헣헤헥헦헧헨헩헪헫헬헭헮헯헰헱헲헳헴헵헶헷헸헹헺헻헼헽헾헿 +혀혁혂혃현혅혆혇혈혉혊혋혌혍혎혏혐협혒혓혔형혖혗혘혙혚혛혜혝혞혟 +혠혡혢혣혤혥혦혧혨혩혪혫혬혭혮혯혰혱혲혳혴혵혶혷호혹혺혻혼혽혾혿 +홀홁홂홃홄홅홆홇홈홉홊홋홌홍홎홏홐홑홒홓화확홖홗환홙홚홛활홝홞홟 +홠홡홢홣홤홥홦홧홨황홪홫홬홭홮홯홰홱홲홳홴홵홶홷홸홹홺홻홼홽홾홿 +횀횁횂횃횄횅횆횇횈횉횊횋회획횎횏횐횑횒횓횔횕횖횗횘횙횚횛횜횝횞횟 +횠횡횢횣횤횥횦횧효횩횪횫횬횭횮횯횰횱횲횳횴횵횶횷횸횹횺횻횼횽횾횿 +훀훁훂훃후훅훆훇훈훉훊훋훌훍훎훏훐훑훒훓훔훕훖훗훘훙훚훛훜훝훞훟 +훠훡훢훣훤훥훦훧훨훩훪훫훬훭훮훯훰훱훲훳훴훵훶훷훸훹훺훻훼훽훾훿 +휀휁휂휃휄휅휆휇휈휉휊휋휌휍휎휏휐휑휒휓휔휕휖휗휘휙휚휛휜휝휞휟 +휠휡휢휣휤휥휦휧휨휩휪휫휬휭휮휯휰휱휲휳휴휵휶휷휸휹휺휻휼휽휾휿 +흀흁흂흃흄흅흆흇흈흉흊흋흌흍흎흏흐흑흒흓흔흕흖흗흘흙흚흛흜흝흞흟 +흠흡흢흣흤흥흦흧흨흩흪흫희흭흮흯흰흱흲흳흴흵흶흷흸흹흺흻흼흽흾흿 +힀힁힂힃힄힅힆힇히힉힊힋힌힍힎힏힐힑힒힓힔힕힖힗힘힙힚힛힜힝힞힟 +힠힡힢힣힤힥힦힧힨힩힪힫힬힭힮힯 + +Free block (U+D7B0-U+D7FF): + +ힰힱힲힳힴힵힶힷힸힹힺힻힼힽힾힿퟀퟁퟂퟃퟄퟅퟆ퟇퟈퟉퟊ퟋퟌퟍퟎퟏퟐퟑퟒퟓퟔퟕퟖퟗퟘퟙퟚퟛퟜퟝퟞퟟퟠퟡퟢퟣퟤퟥퟦퟧퟨퟩퟪퟫퟬퟭퟮퟯ +ퟰퟱퟲퟳퟴퟵퟶퟷퟸퟹퟺퟻ퟼퟽퟾퟿ + +High Surrogates (U+D800-U+DB7F): + +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ + +High Private Use Surrogates (U+DB80-U+DBFF): + +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ + +Low Surrogates (U+DC00-U+DFFF): + +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ +������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ + +Private Use Area (U+E000-U+F8FF): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +CJK Compatibility Ideographs (U+F900-U+FAFF): + +豈更車賈滑串句龜龜契金喇奈懶癩羅蘿螺裸邏樂洛烙珞落酪駱亂卵欄爛蘭 +鸞嵐濫藍襤拉臘蠟廊朗浪狼郎來冷勞擄櫓爐盧老蘆虜路露魯鷺碌祿綠菉錄 +鹿論壟弄籠聾牢磊賂雷壘屢樓淚漏累縷陋勒肋凜凌稜綾菱陵讀拏樂諾丹寧 +怒率異北磻便復不泌數索參塞省葉說殺辰沈拾若掠略亮兩凉梁糧良諒量勵 +呂女廬旅濾礪閭驪麗黎力曆歷轢年憐戀撚漣煉璉秊練聯輦蓮連鍊列劣咽烈 +裂說廉念捻殮簾獵令囹寧嶺怜玲瑩羚聆鈴零靈領例禮醴隸惡了僚寮尿料樂 +燎療蓼遼龍暈阮劉杻柳流溜琉留硫紐類六戮陸倫崙淪輪律慄栗率隆利吏履 +易李梨泥理痢罹裏裡里離匿溺吝燐璘藺隣鱗麟林淋臨立笠粒狀炙識什茶刺 +切度拓糖宅洞暴輻行降見廓兀嗀﨎﨏塚﨑晴﨓﨔凞猪益礼神祥福靖精羽﨟 +蘒﨡諸﨣﨤逸都﨧﨨﨩飯飼館鶴郞隷侮僧免勉勤卑喝嘆器塀墨層屮悔慨憎 +懲敏既暑梅海渚漢煮爫琢碑社祉祈祐祖祝禍禎穀突節練縉繁署者臭艹艹著 +褐視謁謹賓贈辶逸難響頻恵𤋮舘﩮﩯並况全侀充冀勇勺喝啕喙嗢塚墳奄奔 +婢嬨廒廙彩徭惘慎愈憎慠懲戴揄搜摒敖晴朗望杖歹殺流滛滋漢瀞煮瞧爵犯 +猪瑱甆画瘝瘟益盛直睊着磌窱節类絛練缾者荒華蝹襁覆視調諸請謁諾諭謹 +變贈輸遲醙鉶陼難靖韛響頋頻鬒龜𢡊𢡄𣏕㮝䀘䀹𥉉𥳐𧻓齃龎﫚﫛﫜﫝﫞﫟 +﫠﫡﫢﫣﫤﫥﫦﫧﫨﫩﫪﫫﫬﫭﫮﫯﫰﫱﫲﫳﫴﫵﫶﫷﫸﫹﫺﫻﫼﫽﫾﫿 + +Alphabetic Presentation Forms (U+FB00-U+FB4F): + +fffiflffifflſtst﬇﬈﬉﬊﬋﬌﬍﬎﬏﬐﬑﬒ﬓﬔﬕﬖﬗ﬘﬙﬚﬛﬜יִ◌ﬞײַﬠﬡﬢﬣﬤﬥﬦﬧﬨ﬩שׁשׂשּׁשּׂאַאָאּבּגּדּהּוּזּ﬷טּיּךּכּלּ﬽מּ﬿ +נּסּ﭂ףּפּ﭅צּקּרּשּתּוֹבֿכֿפֿﭏ + +Arabic Presentation Forms-A (U+FB50-U+FDFF): + +ﭐﭑﭒﭓﭔﭕﭖﭗﭘﭙﭚﭛﭜﭝﭞﭟﭠﭡﭢﭣﭤﭥﭦﭧﭨﭩﭪﭫﭬﭭﭮﭯﭰﭱﭲﭳﭴﭵﭶﭷﭸﭹﭺﭻﭼﭽﭾﭿﮀﮁﮂﮃﮄﮅﮆﮇﮈﮉﮊﮋﮌﮍﮎﮏ +ﮐﮑﮒﮓﮔﮕﮖﮗﮘﮙﮚﮛﮜﮝﮞﮟﮠﮡﮢﮣﮤﮥﮦﮧﮨﮩﮪﮫﮬﮭﮮﮯﮰﮱ﮲﮳﮴﮵﮶﮷﮸﮹﮺﮻﮼﮽﮾﮿﯀﯁﯂﯃﯄﯅﯆﯇﯈﯉﯊﯋﯌﯍﯎﯏ +﯐﯑﯒ﯓﯔﯕﯖﯗﯘﯙﯚﯛﯜﯝﯞﯟﯠﯡﯢﯣﯤﯥﯦﯧﯨﯩﯪﯫﯬﯭﯮﯯﯰﯱﯲﯳﯴﯵﯶﯷﯸﯹﯺﯻﯼﯽﯾﯿﰀﰁﰂﰃﰄﰅﰆﰇﰈﰉﰊﰋﰌﰍﰎﰏ +ﰐﰑﰒﰓﰔﰕﰖﰗﰘﰙﰚﰛﰜﰝﰞﰟﰠﰡﰢﰣﰤﰥﰦﰧﰨﰩﰪﰫﰬﰭﰮﰯﰰﰱﰲﰳﰴﰵﰶﰷﰸﰹﰺﰻﰼﰽﰾﰿﱀﱁﱂﱃﱄﱅﱆﱇﱈﱉﱊﱋﱌﱍﱎﱏ +ﱐﱑﱒﱓﱔﱕﱖﱗﱘﱙﱚﱛﱜﱝﱞﱟﱠﱡﱢﱣﱤﱥﱦﱧﱨﱩﱪﱫﱬﱭﱮﱯﱰﱱﱲﱳﱴﱵﱶﱷﱸﱹﱺﱻﱼﱽﱾﱿﲀﲁﲂﲃﲄﲅﲆﲇﲈﲉﲊﲋﲌﲍﲎﲏ +ﲐﲑﲒﲓﲔﲕﲖﲗﲘﲙﲚﲛﲜﲝﲞﲟﲠﲡﲢﲣﲤﲥﲦﲧﲨﲩﲪﲫﲬﲭﲮﲯﲰﲱﲲﲳﲴﲵﲶﲷﲸﲹﲺﲻﲼﲽﲾﲿﳀﳁﳂﳃﳄﳅﳆﳇﳈﳉﳊﳋﳌﳍﳎﳏ +ﳐﳑﳒﳓﳔﳕﳖﳗﳘﳙﳚﳛﳜﳝﳞﳟﳠﳡﳢﳣﳤﳥﳦﳧﳨﳩﳪﳫﳬﳭﳮﳯﳰﳱﳲﳳﳴﳵﳶﳷﳸﳹﳺﳻﳼﳽﳾﳿﴀﴁﴂﴃﴄﴅﴆﴇﴈﴉﴊﴋﴌﴍﴎﴏ +ﴐﴑﴒﴓﴔﴕﴖﴗﴘﴙﴚﴛﴜﴝﴞﴟﴠﴡﴢﴣﴤﴥﴦﴧﴨﴩﴪﴫﴬﴭﴮﴯﴰﴱﴲﴳﴴﴵﴶﴷﴸﴹﴺﴻﴼﴽ﴾﴿﵀﵁﵂﵃﵄﵅﵆﵇﵈﵉﵊﵋﵌﵍﵎﵏ +ﵐﵑﵒﵓﵔﵕﵖﵗﵘﵙﵚﵛﵜﵝﵞﵟﵠﵡﵢﵣﵤﵥﵦﵧﵨﵩﵪﵫﵬﵭﵮﵯﵰﵱﵲﵳﵴﵵﵶﵷﵸﵹﵺﵻﵼﵽﵾﵿﶀﶁﶂﶃﶄﶅﶆﶇﶈﶉﶊﶋﶌﶍﶎﶏ +﶐﶑ﶒﶓﶔﶕﶖﶗﶘﶙﶚﶛﶜﶝﶞﶟﶠﶡﶢﶣﶤﶥﶦﶧﶨﶩﶪﶫﶬﶭﶮﶯﶰﶱﶲﶳﶴﶵﶶﶷﶸﶹﶺﶻﶼﶽﶾﶿﷀﷁﷂﷃﷄﷅﷆﷇ﷈﷉﷊﷋﷌﷍﷎﷏ +﷐﷑﷒﷓﷔﷕﷖﷗﷘﷙﷚﷛﷜﷝﷞﷟﷠﷡﷢﷣﷤﷥﷦﷧﷨﷩﷪﷫﷬﷭﷮﷯ﷰﷱﷲﷳﷴﷵﷶﷷﷸﷹﷺﷻ﷼﷽﷾﷿ + +Variation Selectors (U+FE00-U+FE0F): + +◌︀◌︁◌︂◌︃◌︄◌︅◌︆◌︇◌︈◌︉◌︊◌︋◌︌◌︍◌︎◌️ + +Free block (U+FE10-U+FE1F): + +︐︑︒︓︔︕︖︗︘︙︚︛︜︝︞︟ + +Combining Half Marks (U+FE20-U+FE2F): + +◌︠◌︡◌︢◌︧︨︩︪︫︬︭︣︤︥︦︮︯ + +CJK Compatibility Forms (U+FE30-U+FE4F): + +︰︱︲︳︴︵︶︷︸︹︺︻︼︽︾︿﹀﹁﹂﹃﹄﹅﹆﹇﹈﹉﹊﹋﹌﹍﹎﹏ + +Small Form Variants (U+FE50-U+FE6F): + +﹐﹑﹒﹓﹔﹕﹖﹗﹘﹙﹚﹛﹜﹝﹞﹟﹠﹡﹢﹣﹤﹥﹦﹧﹨﹩﹪﹫﹬﹭﹮﹯ + +Arabic Presentation Forms-B (U+FE70-U+FEFF): + +ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯ +ﺰﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯ +ﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ﻽﻾ + +Halfwidth and Fullwidth Forms (U+FF00-U+FFEF): + +＀!"#$%&'()*+,-./0123456789:;<=>? +@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ +`abcdefghijklmnopqrstuvwxyz{|}~⦅ +⦆。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙ +゚ᅠᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒ﾿￀￁ᅡᅢᅣᅤᅥᅦ￈￉ᅧᅨᅩᅪᅫᅬ￐￑ᅭᅮᅯᅰᅱᅲ￘￙ᅳᅴᅵ￝￞ +￟¢£¬ ̄¦¥₩￧│←↑→↓■○￯ + +Specials (U+FFF0-U+FFFF): + +￰￱￲￳￴￵￶￷￸�￾￿ diff --git a/busybox/docs/unit-tests.txt b/busybox/docs/unit-tests.txt new file mode 100644 index 00000000000000..0fb5220869bbc9 --- /dev/null +++ b/busybox/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/busybox/e2fsprogs/Config.src b/busybox/e2fsprogs/Config.src new file mode 100644 index 00000000000000..ad15f470cc617e --- /dev/null +++ b/busybox/e2fsprogs/Config.src @@ -0,0 +1,43 @@ +# +# For a description of the syntax of this configuration file, +# see docs/Kconfig-language.txt. +# + +menu "Linux Ext2 FS Progs" + +INSERT + +### config E2FSCK +### bool "e2fsck" +### default y +### 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 MKE2FS +### bool "mke2fs" +### default y +### help +### mke2fs is used to create an ext2/ext3 filesystem. The normal compat +### symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided. + +### config E2LABEL +### bool "e2label" +### default y +### depends on TUNE2FS +### help +### e2label will display or change the filesystem label on the ext2 +### filesystem located on device. + +### NB: this one is now provided by util-linux/volume_id/* +### config FINDFS +### bool "findfs" +### default y +### 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/busybox/e2fsprogs/Kbuild.src b/busybox/e2fsprogs/Kbuild.src new file mode 100644 index 00000000000000..6b4fb747007c3f --- /dev/null +++ b/busybox/e2fsprogs/Kbuild.src @@ -0,0 +1,9 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +lib-y:= + +INSERT diff --git a/busybox/e2fsprogs/README b/busybox/e2fsprogs/README new file mode 100644 index 00000000000000..eb158e5881da51 --- /dev/null +++ b/busybox/e2fsprogs/README @@ -0,0 +1,12 @@ +Authors and contributors of original e2fsprogs: + +Remy Card +Theodore Ts'o +Stephen C. Tweedie +Andreas Gruenbacher, +Kaz Kylheku +F.W. ten Wolde +Jeremy Fitzhardinge +M.J.E. Mol +Miquel van Smoorenburg +Uwe Ohse diff --git a/busybox/e2fsprogs/chattr.c b/busybox/e2fsprogs/chattr.c new file mode 100644 index 00000000000000..76a5253b62d1b5 --- /dev/null +++ b/busybox/e2fsprogs/chattr.c @@ -0,0 +1,209 @@ +/* vi: set sw=4 ts=4: */ +/* + * chattr.c - Change file attributes on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * 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 + */ +//config:config CHATTR +//config: bool "chattr (3.2 kb)" +//config: default y +//config: help +//config: chattr changes the file attributes on a second extended file system. + +//applet:IF_CHATTR(APPLET_NOEXEC(chattr, chattr, BB_DIR_BIN, BB_SUID_DROP, chattr)) + +//kbuild:lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o + +//usage:#define chattr_trivial_usage +//usage: "[-R] [-v VERSION] [-+=AacDdijsStTu] FILE..." +//usage:#define chattr_full_usage "\n\n" +//usage: "Change ext2 file attributes\n" +//usage: "\n -R Recurse" +//usage: "\n -v VER Set version/generation number" +//-V, -f accepted but ignored +//usage: "\nModifiers:" +//usage: "\n -,+,= Remove/add/set attributes" +//usage: "\nAttributes:" +//usage: "\n A Don't track atime" +//usage: "\n a Append mode only" +//usage: "\n c Enable compress" +//usage: "\n D Write dir contents synchronously" +//usage: "\n d Don't backup with dump" +//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 synchronously" +//usage: "\n t Disable tail-merging of partial blocks with other files" +//usage: "\n u Allow file to be undeleted" + +#include "libbb.h" +#include "e2fs_lib.h" + +#define OPT_ADD 1 +#define OPT_REM 2 +#define OPT_SET 4 +#define OPT_SET_VER 8 + +struct globals { + unsigned long version; + unsigned long af; + unsigned long rf; + int flags; + smallint recursive; +}; + +static unsigned long get_flag(char c) +{ + const char *fp = strchr(e2attr_flags_sname_chattr, c); + if (fp) + return e2attr_flags_value_chattr[fp - e2attr_flags_sname_chattr]; + bb_show_usage(); +} + +static char** decode_arg(char **argv, struct globals *gp) +{ + unsigned long *fl; + const char *arg = *argv; + char opt = *arg; + + fl = &gp->af; + if (opt == '-') { + gp->flags |= OPT_REM; + fl = &gp->rf; + } else if (opt == '+') { + gp->flags |= OPT_ADD; + } else { /* if (opt == '=') */ + gp->flags |= OPT_SET; + } + + while (*++arg) { + if (opt == '-') { +//e2fsprogs-1.43.1 accepts: +// "-RRR", "-RRRv VER" and even "-ARRRva VER" and "-vvv V1 V2 V3" +// but not "-vVER". +// IOW: options are parsed as part of "remove attrs" strings, +// if "v" is seen, next argv[] is VER, even if more opts/attrs follow in this argv[]! + if (*arg == 'R') { + gp->recursive = 1; + continue; + } + if (*arg == 'V') { + /*"verbose and print program version" (nop for now) */; + continue; + } + if (*arg == 'f') { + /*"suppress most error messages" (nop) */; + continue; + } + if (*arg == 'v') { + if (!*++argv) + bb_show_usage(); + gp->version = xatoul(*argv); + gp->flags |= OPT_SET_VER; + continue; + } +//TODO: "-p PROJECT_NUM" ? + /* not a known option, try as an attribute */ + } + *fl |= get_flag(*arg); + } + + return argv; +} + +static void change_attributes(const char *name, struct globals *gp); + +static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp) +{ + char *path = concat_subpath_file(dir_name, de->d_name); + /* path is NULL if de->d_name is "." or "..", else... */ + if (path) { + change_attributes(path, gp); + free(path); + } + return 0; +} + +static void change_attributes(const char *name, struct globals *gp) +{ + unsigned long fsflags; + struct stat st; + + if (lstat(name, &st) != 0) { + bb_perror_msg("stat %s", name); + return; + } + if (S_ISLNK(st.st_mode) && gp->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 (gp->flags & OPT_SET_VER) + if (fsetversion(name, gp->version) != 0) + bb_perror_msg("setting version on %s", name); + + if (gp->flags & OPT_SET) { + fsflags = gp->af; + } else { + if (fgetflags(name, &fsflags) != 0) { + bb_perror_msg("reading flags on %s", name); + goto skip_setflags; + } + /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */ + fsflags &= ~gp->rf; + /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */ + fsflags |= gp->af; +// What is this? And why it's not done for SET case? + if (!S_ISDIR(st.st_mode)) + fsflags &= ~EXT2_DIRSYNC_FL; + } + if (fsetflags(name, fsflags) != 0) + bb_perror_msg("setting flags on %s", name); + + skip_setflags: + if (gp->recursive && S_ISDIR(st.st_mode)) + iterate_on_dir(name, chattr_dir_proc, gp); +} + +int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chattr_main(int argc UNUSED_PARAM, char **argv) +{ + struct globals g; + + memset(&g, 0, sizeof(g)); + + /* parse the args */ + for (;;) { + char *arg = *++argv; + if (!arg) + bb_show_usage(); + if (arg[0] != '-' && arg[0] != '+' && arg[0] != '=') + break; + + argv = decode_arg(argv, &g); + } + /* note: on loop exit, remaining argv[] is never empty */ + + /* run sanity checks on all the arguments given us */ + if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM))) + bb_error_msg_and_die("= is incompatible with - and +"); + if (g.rf & g.af) + bb_error_msg_and_die("can't set and unset a flag"); + if (!g.flags) + bb_error_msg_and_die("must use '-v', =, - or +"); + + /* now run chattr on all the files passed to us */ + do change_attributes(*argv, &g); while (*++argv); + + return EXIT_SUCCESS; +} diff --git a/busybox/e2fsprogs/e2fs_lib.c b/busybox/e2fsprogs/e2fs_lib.c new file mode 100644 index 00000000000000..6ce655be3658c4 --- /dev/null +++ b/busybox/e2fsprogs/e2fs_lib.c @@ -0,0 +1,213 @@ +/* vi: set sw=4 ts=4: */ +/* + * See README for additional information + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +#include "libbb.h" +#include "e2fs_lib.h" + +#define HAVE_EXT2_IOCTLS 1 + +#if INT_MAX == LONG_MAX +#define IF_LONG_IS_SAME(...) __VA_ARGS__ +#define IF_LONG_IS_WIDER(...) +#else +#define IF_LONG_IS_SAME(...) +#define IF_LONG_IS_WIDER(...) __VA_ARGS__ +#endif + +static void close_silently(int fd) +{ + int e = errno; + close(fd); + errno = e; +} + + +/* Iterate a function on each entry of a directory */ +int iterate_on_dir(const char *dir_name, + int FAST_FUNC (*func)(const char *, struct dirent *, void *), + void *private) +{ + DIR *dir; + struct dirent *de; + + dir = opendir(dir_name); + if (dir == NULL) { + return -1; + } + while ((de = readdir(dir)) != NULL) { + func(dir_name, de, private); + } + closedir(dir); + return 0; +} + + +/* Get/set a file version on an ext2 file system */ +int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version) +{ +#if HAVE_EXT2_IOCTLS + int fd, r; + IF_LONG_IS_WIDER(int ver;) + + fd = open(name, O_RDONLY | O_NONBLOCK); + if (fd == -1) + return -1; + if (!get_version) { + IF_LONG_IS_WIDER( + ver = (int) set_version; + r = ioctl(fd, EXT2_IOC_SETVERSION, &ver); + ) + IF_LONG_IS_SAME( + r = ioctl(fd, EXT2_IOC_SETVERSION, (void*)&set_version); + ) + } else { + IF_LONG_IS_WIDER( + r = ioctl(fd, EXT2_IOC_GETVERSION, &ver); + *get_version = ver; + ) + IF_LONG_IS_SAME( + r = ioctl(fd, EXT2_IOC_GETVERSION, (void*)get_version); + ) + } + close_silently(fd); + return r; +#else /* ! HAVE_EXT2_IOCTLS */ + errno = EOPNOTSUPP; + return -1; +#endif /* ! HAVE_EXT2_IOCTLS */ +} + + +/* Get/set a file flags on an ext2 file system */ +int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags) +{ +#if HAVE_EXT2_IOCTLS + struct stat buf; + int fd, r; + IF_LONG_IS_WIDER(int f;) + + if (stat(name, &buf) == 0 /* stat is ok */ + && !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode) + ) { + goto notsupp; + } + fd = open(name, O_RDONLY | O_NONBLOCK); /* neither read nor write asked for */ + if (fd == -1) + return -1; + + if (!get_flags) { + IF_LONG_IS_WIDER( + f = (int) set_flags; + r = ioctl(fd, EXT2_IOC_SETFLAGS, &f); + ) + IF_LONG_IS_SAME( + r = ioctl(fd, EXT2_IOC_SETFLAGS, (void*)&set_flags); + ) + } else { + IF_LONG_IS_WIDER( + r = ioctl(fd, EXT2_IOC_GETFLAGS, &f); + *get_flags = f; + ) + IF_LONG_IS_SAME( + r = ioctl(fd, EXT2_IOC_GETFLAGS, (void*)get_flags); + ) + } + + close_silently(fd); + return r; + notsupp: +#endif /* HAVE_EXT2_IOCTLS */ + errno = EOPNOTSUPP; + return -1; +} + + +/* Print file attributes on an ext2 file system */ +const uint32_t e2attr_flags_value[] = { +#ifdef ENABLE_COMPRESSION + EXT2_COMPRBLK_FL, + EXT2_DIRTY_FL, + EXT2_NOCOMPR_FL, + EXT2_ECOMPR_FL, +#endif + EXT2_INDEX_FL, + EXT2_SECRM_FL, + EXT2_UNRM_FL, + EXT2_SYNC_FL, + EXT2_DIRSYNC_FL, + EXT2_IMMUTABLE_FL, + EXT2_APPEND_FL, + EXT2_NODUMP_FL, + EXT2_NOATIME_FL, + EXT2_COMPR_FL, + EXT3_JOURNAL_DATA_FL, + EXT2_NOTAIL_FL, + EXT2_TOPDIR_FL +}; + +const char e2attr_flags_sname[] ALIGN1 = +#ifdef ENABLE_COMPRESSION + "BZXE" +#endif + "I" + "suSDiadAcjtT"; + +static const char e2attr_flags_lname[] ALIGN1 = +#ifdef ENABLE_COMPRESSION + "Compressed_File" "\0" + "Compressed_Dirty_File" "\0" + "Compression_Raw_Access" "\0" + "Compression_Error" "\0" +#endif + "Indexed_directory" "\0" + "Secure_Deletion" "\0" + "Undelete" "\0" + "Synchronous_Updates" "\0" + "Synchronous_Directory_Updates" "\0" + "Immutable" "\0" + "Append_Only" "\0" + "No_Dump" "\0" + "No_Atime" "\0" + "Compression_Requested" "\0" + "Journaled_Data" "\0" + "No_Tailmerging" "\0" + "Top_of_Directory_Hierarchies" "\0" + /* Another trailing NUL is added by compiler */; + +void print_e2flags(FILE *f, unsigned long flags, unsigned options) +{ + const uint32_t *fv; + const char *fn; + + fv = e2attr_flags_value; + if (options & PFOPT_LONG) { + int first = 1; + fn = e2attr_flags_lname; + do { + if (flags & *fv) { + if (!first) + fputs(", ", f); + fputs(fn, f); + first = 0; + } + fv++; + fn += strlen(fn) + 1; + } while (*fn); + if (first) + fputs("---", f); + } else { + fn = e2attr_flags_sname; + do { + char c = '-'; + if (flags & *fv) + c = *fn; + fputc(c, f); + fv++; + fn++; + } while (*fn); + } +} diff --git a/busybox/e2fsprogs/e2fs_lib.h b/busybox/e2fsprogs/e2fs_lib.h new file mode 100644 index 00000000000000..ae28c353b773bf --- /dev/null +++ b/busybox/e2fsprogs/e2fs_lib.h @@ -0,0 +1,47 @@ +/* vi: set sw=4 ts=4: */ +/* + * See README for additional information + * + * This file can be redistributed under the terms of the GNU Library General + * Public License + */ + +/* Constants and structures */ +#include "bb_e2fs_defs.h" + +PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN + +/* Iterate a function on each entry of a directory */ +int iterate_on_dir(const char *dir_name, + int FAST_FUNC (*func)(const char *, struct dirent *, void *), + void *private); + +/* Get/set a file version on an ext2 file system */ +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) + +/* Get/set a file flags on an ext2 file system */ +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) + +/* Must be 1 for compatibility with 'int long_format'. */ +#define PFOPT_LONG 1 +/* Print file attributes on an ext2 file system */ +void print_e2flags(FILE *f, unsigned long flags, unsigned options); + +extern const uint32_t e2attr_flags_value[]; +extern const char e2attr_flags_sname[]; + +/* If you plan to ENABLE_COMPRESSION, see e2fs_lib.c and chattr.c - */ +/* make sure that chattr doesn't accept bad options! */ +#ifdef ENABLE_COMPRESSION +#define e2attr_flags_value_chattr (&e2attr_flags_value[5]) +#define e2attr_flags_sname_chattr (&e2attr_flags_sname[5]) +#else +#define e2attr_flags_value_chattr (&e2attr_flags_value[1]) +#define e2attr_flags_sname_chattr (&e2attr_flags_sname[1]) +#endif + +POP_SAVED_FUNCTION_VISIBILITY diff --git a/busybox/e2fsprogs/fsck.c b/busybox/e2fsprogs/fsck.c new file mode 100644 index 00000000000000..f5aa3dbe47b3aa --- /dev/null +++ b/busybox/e2fsprogs/fsck.c @@ -0,0 +1,1115 @@ +/* vi: set sw=4 ts=4: */ +/* + * fsck --- 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, + * + * 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. + */ + +/* All filesystem specific hooks have been removed. + * If filesystem cannot be determined, we will execute + * "fsck.auto". Currently this also happens if you specify + * UUID=xxx or LABEL=xxx as an object to check. + * Detection code for that is also probably has to be in fsck.auto. + * + * In other words, this is _really_ is just a driver program which + * 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 (6.7 kb)" +//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] [-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" +//usage: "\n -N Don't execute, just show what would be done" +//usage: "\n -P With -A, check filesystems in parallel" +//usage: "\n -R With -A, skip the root filesystem" +//usage: "\n -T Don't show title on startup" +//usage: "\n -V Verbose" +//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 + * API for fsck.something, NOT ad-hoc hacks in generic fsck. */ +#define DO_PROGRESS_INDICATOR 0 + +/* fsck 1.41.4 (27-Jan-2009) manpage says: + * 0 - No errors + * 1 - File system errors corrected + * 2 - System should be rebooted + * 4 - File system errors left uncorrected + * 8 - Operational error + * 16 - Usage or syntax error + * 32 - Fsck canceled by user request + * 128 - Shared library error + */ +#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 */ + +/* + * Internal structure for mount table entries. + */ +struct fs_info { + struct fs_info *next; + char *device; + char *mountpt; + char *type; + char *opts; + int passno; + int flags; +}; + +#define FLAG_DONE 1 +#define FLAG_PROGRESS 2 +/* + * Structure to allow exit codes to be stored + */ +struct fsck_instance { + struct fsck_instance *next; + int pid; + int flags; +#if DO_PROGRESS_INDICATOR + time_t start_time; +#endif + char *prog; + char *device; + char *base_device; /* /dev/hda for /dev/hdaN etc */ +}; + +static const char ignored_types[] ALIGN1 = + "ignore\0" + "iso9660\0" + "nfs\0" + "proc\0" + "sw\0" + "swap\0" + "tmpfs\0" + "devpts\0"; + +#if 0 +static const char really_wanted[] ALIGN1 = + "minix\0" + "ext2\0" + "ext3\0" + "jfs\0" + "reiserfs\0" + "xiafs\0" + "xfs\0"; +#endif + +#define BASE_MD "/dev/md" + +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 + 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 + smallint progress; + int progress_fd; +#endif + + 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 + * 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. + */ +#if ENABLE_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", NULL +}; +#endif + +static char *base_device(const char *device) +{ + char *str, *cp; +#if ENABLE_FEATURE_DEVFS + const char *const *hier; + const char *disk; + int len; +#endif + str = xstrdup(device); + + /* Skip over "/dev/"; if it's not present, give up */ + cp = skip_dev_pfx(str); + if (cp == str) + goto errout; + + /* + * 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 (is_prefixed_with(cp, "rd/")) { + cp += 3; + if (cp[0] != 'c' || !isdigit(cp[1]) + || cp[2] != 'd' || !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; + } + +#if ENABLE_FEATURE_DEVFS + /* Now let's handle devfs (ugh) names */ + len = 0; + if (is_prefixed_with(cp, "ide/")) + len = 4; + if (is_prefixed_with(cp, "scsi/")) + 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++) { + cp = is_prefixed_with(cp, *hier); + if (!cp) + goto errout; + while (*cp != '/' && *cp != '\0') { + if (!isdigit(*cp)) + goto errout; + cp++; + } +//FIXME: what if *cp = '\0' now? cp++ moves past it!!! + cp++; + } + cp[-1] = '\0'; + return str; + } + + /* Now handle devfs /dev/disc or /dev/disk names */ + disk = NULL; + if (is_prefixed_with(cp, "discs/")) + disk = "disc"; + else if (is_prefixed_with(cp, "disks/")) + disk = "disk"; + if (disk) { + cp += 6; + cp = is_prefixed_with(cp, disk); + if (!cp) + goto errout; + while (*cp != '/' && *cp != '\0') { + if (!isdigit(*cp)) + goto errout; + cp++; + } + *cp = '\0'; + return str; + } +#endif + errout: + free(str); + return NULL; +} + +static void free_instance(struct fsck_instance *p) +{ + free(p->prog); + free(p->device); + free(p->base_device); + free(p); +} + +static struct fs_info *create_fs_device(const char *device, const char *mntpnt, + const char *type, const char *opts, + int passno) +{ + struct fs_info *fs; + + fs = xzalloc(sizeof(*fs)); + fs->device = xstrdup(device); + fs->mountpt = xstrdup(mntpnt); + if (strchr(type, ',')) + type = (char *)"auto"; + fs->type = xstrdup(type); + fs->opts = xstrdup(opts ? opts : ""); + fs->passno = passno < 0 ? 1 : passno; + /*fs->flags = 0; */ + /*fs->next = NULL; */ + + if (!G.filesys_info) + G.filesys_info = fs; + else + G.filesys_last->next = fs; + G.filesys_last = fs; + + return fs; +} + +/* Load the filesystem database from /etc/fstab */ +static void load_fs_info(const char *filename) +{ + FILE *fstab; + struct mntent mte; + char buf[1024]; + + fstab = setmntent(filename, "r"); + if (!fstab) { + bb_perror_msg("can't read '%s'", filename); + return; + } + + // Loop through entries + 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, + mte.mnt_type, mte.mnt_opts, + mte.mnt_passno); + } + endmntent(fstab); +} + +/* Lookup filesys in /etc/fstab and return the corresponding entry. */ +static struct fs_info *lookup(char *filesys) +{ + struct fs_info *fs; + + for (fs = G.filesys_info; fs; fs = fs->next) { + if (strcmp(filesys, fs->device) == 0 + || (fs->mountpt && strcmp(filesys, fs->mountpt) == 0) + ) + break; + } + + return fs; +} + +#if DO_PROGRESS_INDICATOR +static int progress_active(void) +{ + struct fsck_instance *inst; + + for (inst = G.instance_list; inst; inst = inst->next) { + if (inst->flags & FLAG_DONE) + continue; + if (inst->flags & FLAG_PROGRESS) + return 1; + } + return 0; +} +#endif + + +/* + * Send a signal to all outstanding fsck child processes + */ +static void kill_all_if_got_signal(void) +{ + struct fsck_instance *inst; + + if (!bb_got_signal || G.kill_sent) + return; + + for (inst = G.instance_list; inst; inst = inst->next) { + if (inst->flags & FLAG_DONE) + continue; + kill(inst->pid, SIGTERM); + } + G.kill_sent = 1; +} + +/* + * Wait for one child process to exit; when it does, unlink it from + * the list of executing child processes, free, and return its exit status. + * If there is no exited child, return -1. + */ +static int wait_one(int flags) +{ + int status; + int exitcode; + struct fsck_instance *inst, *prev; + pid_t pid; + + if (!G.instance_list) + return -1; + /* if (G.noexecute) { already returned -1; } */ + + while (1) { + pid = waitpid(-1, &status, flags); + kill_all_if_got_signal(); + if (pid == 0) /* flags == WNOHANG and no children exited */ + return -1; + if (pid < 0) { + if (errno == EINTR) + continue; + if (errno == ECHILD) { /* paranoia */ + bb_error_msg("wait: no more children"); + return -1; + } + bb_perror_msg("wait"); + continue; + } + prev = NULL; + inst = G.instance_list; + do { + if (inst->pid == pid) + goto child_died; + prev = inst; + inst = inst->next; + } while (inst); + } + child_died: + + exitcode = WEXITSTATUS(status); + if (WIFSIGNALED(status)) { + unsigned sig; + sig = WTERMSIG(status); + exitcode = EXIT_UNCORRECTED; + if (sig != SIGINT) { + printf("Warning: %s %s terminated " + "by signal %u\n", + inst->prog, inst->device, sig); + exitcode = EXIT_ERROR; + } + } + +#if DO_PROGRESS_INDICATOR + if (progress && (inst->flags & FLAG_PROGRESS) && !progress_active()) { + struct fsck_instance *inst2; + for (inst2 = G.instance_list; inst2; inst2 = inst2->next) { + if (inst2->flags & FLAG_DONE) + continue; + if (strcmp(inst2->type, "ext2") != 0 + && strcmp(inst2->type, "ext3") != 0 + ) { + continue; + } + /* ext[23], we will send USR1 + * (request to start displaying progress bar) + * + * 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(NULL) - 1) + sleep(1); + kill(inst2->pid, SIGUSR1); + inst2->flags |= FLAG_PROGRESS; + break; + } + } +#endif + + if (prev) + prev->next = inst->next; + else + G.instance_list = inst->next; + if (G.verbose > 1) + printf("Finished with %s (exit status %u)\n", + inst->device, exitcode); + G.num_running--; + free_instance(inst); + + return exitcode; +} + +/* + * Wait until all executing child processes have exited; return the + * logical OR of all of their exit code values. + */ +#define FLAG_WAIT_ALL 0 +#define FLAG_WAIT_ATLEAST_ONE WNOHANG +static int wait_many(int flags) +{ + int exit_status; + int global_status = 0; + int wait_flags = 0; + + while ((exit_status = wait_one(wait_flags)) != -1) { + global_status |= exit_status; + wait_flags |= flags; + } + return global_status; +} + +/* + * Execute a particular fsck program, and link it into the list of + * child processes we are waiting for. + */ +static void execute(const char *type, const char *device, + const char *mntpt /*, int interactive */) +{ + int i; + struct fsck_instance *inst; + pid_t pid; + + G.args[0] = xasprintf("fsck.%s", type); + +#if DO_PROGRESS_INDICATOR + if (progress && !progress_active()) { + if (strcmp(type, "ext2") == 0 + || strcmp(type, "ext3") == 0 + ) { + G.args[XXX] = xasprintf("-C%d", progress_fd); /* 1 */ + inst->flags |= FLAG_PROGRESS; + } + } +#endif + + G.args[G.num_args - 2] = (char*)device; + /* G.args[G.num_args - 1] = NULL; - already is */ + + if (G.verbose || G.noexecute) { + printf("[%s (%d) -- %s]", G.args[0], G.num_running, + mntpt ? mntpt : device); + for (i = 0; G.args[i]; i++) + printf(" %s", G.args[i]); + bb_putchar('\n'); + } + + /* Fork and execute the correct program. */ + pid = -1; + if (!G.noexecute) { + pid = spawn(G.args); + if (pid < 0) + bb_simple_perror_msg(G.args[0]); + } + +#if DO_PROGRESS_INDICATOR + free(G.args[XXX]); +#endif + + /* No child, so don't record an instance */ + if (pid <= 0) { + free(G.args[0]); + return; + } + + inst = xzalloc(sizeof(*inst)); + inst->pid = pid; + inst->prog = G.args[0]; + inst->device = xstrdup(device); + inst->base_device = base_device(device); +#if DO_PROGRESS_INDICATOR + inst->start_time = time(NULL); +#endif + + /* Add to the list of running fsck's. + * (was adding to the end, but adding to the front is simpler...) */ + inst->next = G.instance_list; + G.instance_list = inst; +} + +/* + * 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 "auto". + */ +static void fsck_device(struct fs_info *fs /*, int interactive */) +{ + const char *type; + + if (strcmp(fs->type, "auto") != 0) { + type = fs->type; + if (G.verbose > 2) + printf("using filesystem type '%s' %s\n", + type, "from fstab"); + } 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 = G.fstype; + if (G.verbose > 2) + printf("using filesystem type '%s' %s\n", + type, "from -t"); + } else { + type = "auto"; + if (G.verbose > 2) + printf("using filesystem type '%s' %s\n", + type, "(default)"); + } + + G.num_running++; + execute(type, fs->device, fs->mountpt /*, interactive */); +} + +/* + * 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 (G.force_all_parallel) + return 0; + +#ifdef BASE_MD + /* Don't check a soft raid disk with any other disk */ + if (G.instance_list + && (is_prefixed_with(G.instance_list->device, BASE_MD) + || is_prefixed_with(device, BASE_MD)) + ) { + 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 (G.instance_list != NULL); + + for (inst = G.instance_list; inst; inst = inst->next) { + if (!inst->base_device || strcmp(base, inst->base_device) == 0) { + free(base); + return 1; + } + } + + free(base); + return 0; +} + +/* + * 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 *s; + int len; + + if (!optlist) + return 0; + + len = strlen(opt); + s = optlist - 1; + while (1) { + s = strstr(s + 1, opt); + if (!s) + return 0; + /* neither "opt.." nor "xxx,opt.."? */ + if (s != optlist && s[-1] != ',') + continue; + /* neither "..opt" nor "..opt,xxx"? */ + if (s[len] != '\0' && s[len] != ',') + continue; + return 1; + } +} + +/* See if the filesystem matches the criteria given by the -t option */ +static int fs_match(struct fs_info *fs) +{ + int n, ret, checked_type; + char *cp; + + if (!G.fs_type_list) + return 1; + + ret = 0; + checked_type = 0; + n = 0; + while (1) { + cp = G.fs_type_list[n]; + if (!cp) + break; + switch (G.fs_type_flag[n]) { + case FS_TYPE_FLAG_NORMAL: + checked_type++; + if (strcmp(cp, fs->type) == 0) + ret = 1; + break; + case FS_TYPE_FLAG_NEGOPT: + if (opt_in_list(cp, fs->opts)) + return 0; + break; + case FS_TYPE_FLAG_OPT: + if (!opt_in_list(cp, fs->opts)) + return 0; + break; + } + n++; + } + if (checked_type == 0) + return 1; + + return (G.fs_type_negated ? !ret : ret); +} + +/* Check if we should ignore this filesystem. */ +static int ignore(struct fs_info *fs) +{ + /* + * If the pass number is 0, ignore it. + */ + if (fs->passno == 0) + return 1; + + /* + * If a specific fstype is specified, and it doesn't match, + * ignore it. + */ + if (!fs_match(fs)) + return 1; + + /* Are we ignoring this type? */ + if (index_in_strings(ignored_types, fs->type) >= 0) + return 1; + + /* We can and want to check this file system type. */ + return 0; +} + +/* Check all file systems, using the /etc/fstab table. */ +static int check_all(void) +{ + struct fs_info *fs; + int status = EXIT_OK; + smallint not_done_yet; + smallint pass_done; + int passno; + + if (G.verbose) + puts("Checking all filesystems"); + + /* + * 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 = G.filesys_info; fs; fs = fs->next) + if (ignore(fs)) + fs->flags |= FLAG_DONE; + + /* + * Find and check the root filesystem. + */ + if (!G.parallel_root) { + for (fs = G.filesys_info; fs; fs = fs->next) { + if (LONE_CHAR(fs->mountpt, '/')) { + if (!G.skip_root && !ignore(fs)) { + fsck_device(fs /*, 1*/); + status |= wait_many(FLAG_WAIT_ALL); + if (status > EXIT_NONDESTRUCT) + return status; + } + fs->flags |= FLAG_DONE; + break; + } + } + } + /* + * This is for the bone-headed user who has root + * filesystem listed twice. + * "Skip root" will skip _all_ root entries. + */ + if (G.skip_root) + for (fs = G.filesys_info; fs; fs = fs->next) + if (LONE_CHAR(fs->mountpt, '/')) + fs->flags |= FLAG_DONE; + + not_done_yet = 1; + passno = 1; + while (not_done_yet) { + not_done_yet = 0; + pass_done = 1; + + for (fs = G.filesys_info; fs; fs = fs->next) { + if (bb_got_signal) + break; + if (fs->flags & FLAG_DONE) + continue; + /* + * If the filesystem's pass number is higher + * than the current pass number, then we didn't + * do it yet. + */ + if (fs->passno > passno) { + not_done_yet = 1; + 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 /*, G.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 (G.serialize + || (G.num_running >= G.max_running) + ) { + pass_done = 0; + break; + } + } + if (bb_got_signal) + break; + 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 (G.verbose > 1) + puts("----------------------------------"); + passno++; + } else + not_done_yet = 1; + } + kill_all_if_got_signal(); + status |= wait_many(FLAG_WAIT_ATLEAST_ONE); + return status; +} + +/* + * Deal with the fsck -t argument. + * Huh, for mount "-t novfat,nfs" means "neither vfat nor nfs"! + * Why here we require "-t novfat,nonfs" ?? + */ +static void compile_fs_type(char *fs_type) +{ + char *s; + int num = 2; + smallint negate; + + s = fs_type; + while ((s = strchr(s, ','))) { + num++; + s++; + } + + 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; + while (1) { + char *comma; + + negate = 0; + if (s[0] == 'n' && s[1] == 'o') { /* "no.." */ + s += 2; + negate = 1; + } else if (s[0] == '!') { + s++; + negate = 1; + } + + if (strcmp(s, "loop") == 0) + /* loop is really short-hand for opts=loop */ + goto loop_special_case; + if (is_prefixed_with(s, "opts=")) { + s += 5; + loop_special_case: + G.fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT; + } else { + 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 = strchrnul(s, ','); + G.fs_type_list[num++] = xstrndup(s, comma-s); + if (*comma == '\0') + break; + s = comma + 1; + } +} + +static char **new_args(void) +{ + 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; +int fsck_main(int argc UNUSED_PARAM, char **argv) +{ + int i, status; + /*int interactive;*/ + struct fs_info *fs; + const char *fstab; + char *tmp; + char **devices; + int num_devices; + smallint opts_for_fsck; + 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); + + setbuf(stdout, NULL); + + opts_for_fsck = doall = notitle = 0; + devices = NULL; + num_devices = 0; + new_args(); /* G.args[0] = NULL, will be replaced by fsck. */ + /* G.instance_list = NULL; - in bss, so already zeroed */ + + while (*++argv) { + int j; + int optpos; + char *options; + char *arg = *argv; + + /* "/dev/blk" or "/path" or "UUID=xxx" or "LABEL=xxx" */ + if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) { +// FIXME: must check that arg is a blkdev, or resolve +// "/path", "UUID=xxx" or "LABEL=xxx" into block device name +// ("UUID=xxx"/"LABEL=xxx" can probably shifted to fsck.auto duties) + devices = xrealloc_vector(devices, 2, num_devices); + devices[num_devices++] = arg; + continue; + } + + if (arg[0] != '-' || opts_for_fsck) { + *new_args() = arg; + continue; + } + + if (LONE_CHAR(arg + 1, '-')) { /* "--" ? */ + opts_for_fsck = 1; + continue; + } + + optpos = 0; + options = NULL; + for (j = 1; arg[j]; j++) { + switch (arg[j]) { + case 'A': + doall = 1; + break; +#if DO_PROGRESS_INDICATOR + case 'C': + progress = 1; + if (arg[++j]) { /* -Cn */ + progress_fd = xatoi_positive(&arg[j]); + goto next_arg; + } + /* -C n */ + if (!*++argv) + bb_show_usage(); + progress_fd = xatoi_positive(*argv); + goto next_arg; +#endif + case 'V': + G.verbose++; + break; + case 'N': + G.noexecute = 1; + break; + case 'R': + G.skip_root = 1; + break; + case 'T': + notitle = 1; + break; +/* case 'M': + like_mount = 1; + break; */ + case 'P': + G.parallel_root = 1; + break; + case 's': + G.serialize = 1; + break; + case 't': + if (G.fstype) + bb_show_usage(); + if (arg[++j]) + tmp = &arg[j]; + else if (*++argv) + tmp = *argv; + else + bb_show_usage(); + G.fstype = xstrdup(tmp); + compile_fs_type(G.fstype); + goto next_arg; + case '?': + bb_show_usage(); + break; + default: + optpos++; + /* one extra for '\0' */ + options = xrealloc(options, optpos + 2); + options[optpos] = arg[j]; + break; + } + } + next_arg: + if (optpos) { + options[0] = '-'; + options[optpos + 1] = '\0'; + *new_args() = options; + } + } + if (getenv("FSCK_FORCE_ALL_PARALLEL")) + G.force_all_parallel = 1; + tmp = getenv("FSCK_MAX_INST"); + G.max_running = INT_MAX; + if (tmp) + G.max_running = xatoi(tmp); + new_args(); /* G.args[G.num_args - 2] will be replaced by */ + new_args(); /* G.args[G.num_args - 1] is the last, NULL element */ + + if (!notitle) + puts("fsck (busybox "BB_VER")"); + + /* Even plain "fsck /dev/hda1" needs fstab to get fs type, + * so we are scanning it anyway */ + fstab = getenv("FSTAB_FILE"); + if (!fstab) + fstab = "/etc/fstab"; + load_fs_info(fstab); + + /*interactive = (num_devices == 1) | G.serialize;*/ + + if (num_devices == 0) + /*interactive =*/ G.serialize = doall = 1; + if (doall) + return check_all(); + + status = 0; + for (i = 0; i < num_devices; i++) { + if (bb_got_signal) { + kill_all_if_got_signal(); + break; + } + + fs = lookup(devices[i]); + if (!fs) + fs = create_fs_device(devices[i], "", "auto", NULL, -1); + fsck_device(fs /*, interactive */); + + if (G.serialize + || (G.num_running >= G.max_running) + ) { + int exit_status = wait_one(0); + if (exit_status >= 0) + status |= exit_status; + if (G.verbose > 1) + puts("----------------------------------"); + } + } + status |= wait_many(FLAG_WAIT_ALL); + return status; +} diff --git a/busybox/e2fsprogs/lsattr.c b/busybox/e2fsprogs/lsattr.c new file mode 100644 index 00000000000000..56c1187c172ee6 --- /dev/null +++ b/busybox/e2fsprogs/lsattr.c @@ -0,0 +1,123 @@ +/* vi: set sw=4 ts=4: */ +/* + * lsattr.c - List file attributes on an ext2 file system + * + * Copyright (C) 1993, 1994 Remy Card + * 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 + */ +//config:config LSATTR +//config: bool "lsattr (5 kb)" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: lsattr lists the file attributes on a second extended file system. + +//applet:IF_LSATTR(APPLET_NOEXEC(lsattr, lsattr, BB_DIR_BIN, BB_SUID_DROP, lsattr)) +/* ls is NOEXEC, so we should be too! ;) */ + +//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 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 version/generation number" + +#include "libbb.h" +#include "e2fs_lib.h" + +enum { + OPT_RECUR = 0x1, + OPT_ALL = 0x2, + OPT_DIRS_OPT = 0x4, + OPT_PF_LONG = 0x8, + OPT_GENERATION = 0x10, +}; + +static void list_attributes(const char *name) +{ + unsigned long fsflags; + unsigned long generation; + + if (fgetflags(name, &fsflags) != 0) + goto read_err; + + if (option_mask32 & OPT_GENERATION) { + if (fgetversion(name, &generation) != 0) + goto read_err; + printf("%5lu ", generation); + } + + if (option_mask32 & 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 FAST_FUNC lsattr_dir_proc(const char *dir_name, + struct dirent *de, + void *private UNUSED_PARAM) +{ + struct stat st; + char *path; + + path = concat_path_file(dir_name, de->d_name); + + if (lstat(path, &st) != 0) + bb_perror_msg("stat %s", path); + else if (de->d_name[0] != '.' || (option_mask32 & OPT_ALL)) { + list_attributes(path); + if (S_ISDIR(st.st_mode) && (option_mask32 & OPT_RECUR) + && !DOT_OR_DOTDOT(de->d_name) + ) { + printf("\n%s:\n", path); + iterate_on_dir(path, lsattr_dir_proc, NULL); + bb_putchar('\n'); + } + } + + free(path); + return 0; +} + +static void lsattr_args(const char *name) +{ + struct stat st; + + if (lstat(name, &st) == -1) { + bb_perror_msg("stat %s", name); + } else if (S_ISDIR(st.st_mode) && !(option_mask32 & OPT_DIRS_OPT)) { + iterate_on_dir(name, lsattr_dir_proc, NULL); + } else { + list_attributes(name); + } +} + +int lsattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int lsattr_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, "Radlv"); + argv += optind; + + if (!*argv) + *--argv = (char*)"."; + do lsattr_args(*argv++); while (*argv); + + return EXIT_SUCCESS; +} diff --git a/busybox/e2fsprogs/tune2fs.c b/busybox/e2fsprogs/tune2fs.c new file mode 100644 index 00000000000000..a1caf011c88ba8 --- /dev/null +++ b/busybox/e2fsprogs/tune2fs.c @@ -0,0 +1,119 @@ +/* vi: set sw=4 ts=4: */ +/* + * tune2fs: utility to modify EXT2 filesystem + * + * Busybox'ed (2009) by Vladimir Dronnikov + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config TUNE2FS +//config: bool "tune2fs (4.4 kb)" +//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_NOEXEC(tune2fs, tune2fs, BB_DIR_SBIN, BB_SUID_DROP, tune2fs)) + +//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 +#include "bb_e2fs_defs.h" + +// storage helpers +char BUG_wrong_field_size(void); +#define STORE_LE(field, value) \ +do { \ + if (sizeof(field) == 4) \ + field = SWAP_LE32(value); \ + else if (sizeof(field) == 2) \ + field = SWAP_LE16(value); \ + else if (sizeof(field) == 1) \ + field = (value); \ + else \ + BUG_wrong_field_size(); \ +} while (0) + +#define FETCH_LE32(field) \ + (sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size()) + +enum { + OPT_L = 1 << 0, // label + OPT_c = 1 << 1, // max mount count + OPT_i = 1 << 2, // check interval + OPT_C = 1 << 3, // current mount count +}; + +int tune2fs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tune2fs_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opts; + const char *label, *str_c, *str_i, *str_C; + struct ext2_super_block *sb; + int fd; + + opts = getopt32(argv, "^" "L:c:i:C:" "\0" "=1", &label, &str_c, &str_i, &str_C); + if (!opts) + bb_show_usage(); + argv += optind; // argv[0] -- device + + // read superblock + fd = xopen(argv[0], O_RDWR); + xlseek(fd, 1024, SEEK_SET); + sb = xzalloc(1024); + xread(fd, sb, 1024); + + // mangle superblock + //STORE_LE(sb->s_wtime, time(NULL)); - why bother? + + if (opts & OPT_C) { + int n = xatoi_range(str_C, 1, 0xfffe); + STORE_LE(sb->s_mnt_count, (unsigned)n); + } + + // set the label + if (opts & OPT_L) + safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name)); + + if (opts & OPT_c) { + int n = xatoi_range(str_c, -1, 0xfffe); + if (n == 0) + n = -1; + STORE_LE(sb->s_max_mnt_count, (unsigned)n); + } + + if (opts & OPT_i) { + unsigned n = xatou_range(str_i, 0, (unsigned)0xffffffff / (24*60*60)) * 24*60*60; + STORE_LE(sb->s_checkinterval, n); + } + + // write superblock + xlseek(fd, 1024, SEEK_SET); + xwrite(fd, sb, 1024); + + if (ENABLE_FEATURE_CLEAN_UP) { + free(sb); + } + + xclose(fd); + return EXIT_SUCCESS; +} diff --git a/busybox/editors/Config.src b/busybox/editors/Config.src new file mode 100644 index 00000000000000..3b2e4a6c0d8a99 --- /dev/null +++ b/busybox/editors/Config.src @@ -0,0 +1,18 @@ +# +# For a description of the syntax of this configuration file, +# see docs/Kconfig-language.txt. +# + +menu "Editors" + +INSERT + +config FEATURE_ALLOW_EXEC + bool "Allow vi and awk to execute shell commands" + default y + depends on VI || AWK + help + Enables vi and awk features which allow user to execute + shell commands (using system() C call). + +endmenu diff --git a/busybox/editors/Kbuild.src b/busybox/editors/Kbuild.src new file mode 100644 index 00000000000000..6b4fb747007c3f --- /dev/null +++ b/busybox/editors/Kbuild.src @@ -0,0 +1,9 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# Licensed under GPLv2, see file LICENSE in this source tree. + +lib-y:= + +INSERT diff --git a/busybox/editors/awk.c b/busybox/editors/awk.c new file mode 100644 index 00000000000000..bafc9ba1d382b6 --- /dev/null +++ b/busybox/editors/awk.c @@ -0,0 +1,3301 @@ +/* vi: set sw=4 ts=4: */ +/* + * awk implementation for busybox + * + * Copyright (C) 2002 by Dmitry Zakharov + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config AWK +//config: bool "awk (22 kb)" +//config: default y +//config: help +//config: Awk is used as a pattern scanning and processing language. +//config: +//config:config FEATURE_AWK_LIBM +//config: bool "Enable math functions (requires libm)" +//config: default y +//config: depends on AWK +//config: help +//config: Enable math functions of the Awk programming language. +//config: NOTE: This requires libm to be present for linking. +//config: +//config:config FEATURE_AWK_GNU_EXTENSIONS +//config: bool "Enable a few GNU extensions" +//config: default y +//config: depends on AWK +//config: help +//config: Enable a few features from gawk: +//config: * command line option -e AWK_PROGRAM +//config: * simultaneous use of -f and -e on the command line. +//config: This enables the use of awk library files. +//config: Example: awk -f mylib.awk -e '{print myfunction($1);}' ... + +//applet:IF_AWK(APPLET_NOEXEC(awk, awk, BB_DIR_USR_BIN, BB_SUID_DROP, awk)) + +//kbuild:lib-$(CONFIG_AWK) += awk.o + +//usage:#define awk_trivial_usage +//usage: "[OPTIONS] [AWK_PROGRAM] [FILE]..." +//usage:#define awk_full_usage "\n\n" +//usage: " -v VAR=VAL Set variable" +//usage: "\n -F SEP Use SEP as field separator" +//usage: "\n -f FILE Read program from FILE" +//usage: IF_FEATURE_AWK_GNU_EXTENSIONS( +//usage: "\n -e AWK_PROGRAM" +//usage: ) + +#include "libbb.h" +#include "xregex.h" +#include + +/* This is a NOEXEC applet. Be very careful! */ + + +/* If you comment out one of these below, it will be #defined later + * to perform debug printfs to stderr: */ +#define debug_printf_walker(...) do {} while (0) +#define debug_printf_eval(...) do {} while (0) +#define debug_printf_parse(...) do {} while (0) + +#ifndef debug_printf_walker +# define debug_printf_walker(...) (fprintf(stderr, __VA_ARGS__)) +#endif +#ifndef debug_printf_eval +# define debug_printf_eval(...) (fprintf(stderr, __VA_ARGS__)) +#endif +#ifndef debug_printf_parse +# define debug_printf_parse(...) (fprintf(stderr, __VA_ARGS__)) +#endif + + +/* "+": stop on first non-option: + * $ awk 'BEGIN { for(i=1; i >> */ +#define TC_UOPPOST (1 << 4) /* unary postfix operator */ +#define TC_UOPPRE1 (1 << 5) /* unary prefix operator */ +#define TC_BINOPX (1 << 6) /* two-opnd operator */ +#define TC_IN (1 << 7) +#define TC_COMMA (1 << 8) +#define TC_PIPE (1 << 9) /* input redirection pipe */ +#define TC_UOPPRE2 (1 << 10) /* unary prefix operator */ +#define TC_ARRTERM (1 << 11) /* ] */ +#define TC_GRPSTART (1 << 12) /* { */ +#define TC_GRPTERM (1 << 13) /* } */ +#define TC_SEMICOL (1 << 14) +#define TC_NEWLINE (1 << 15) +#define TC_STATX (1 << 16) /* ctl statement (for, next...) */ +#define TC_WHILE (1 << 17) +#define TC_ELSE (1 << 18) +#define TC_BUILTIN (1 << 19) +/* 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) + +/* combined token classes */ +#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_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_LENGTH | TC_GETLINE \ + | TC_FUNCDECL | TC_BEGIN | TC_END) + +/* discard newlines after these */ +#define TC_NOTERM (TC_COMMA | TC_GRPSTART | TC_GRPTERM \ + | TC_BINOP | TC_OPTERM) + +/* what can expression begin with */ +#define TC_OPSEQ (TC_OPERAND | TC_UOPPRE | TC_REGEXP) +/* what can group begin with */ +#define TC_GRPSEQ (TC_OPSEQ | TC_OPTERM | TC_STATEMNT | TC_GRPSTART) + +/* if previous token class is CONCAT1 and next is CONCAT2, concatenation */ +/* operator is inserted between them */ +#define TC_CONCAT1 (TC_VARIABLE | TC_ARRTERM | TC_SEQTERM \ + | TC_STRING | TC_NUMBER | TC_UOPPOST) +#define TC_CONCAT2 (TC_OPERAND | TC_UOPPRE) + +#define OF_RES1 0x010000 +#define OF_RES2 0x020000 +#define OF_STR1 0x040000 +#define OF_STR2 0x080000 +#define OF_NUM1 0x100000 +#define OF_CHECKED 0x200000 + +/* combined operator flags */ +#define xx 0 +#define xV OF_RES2 +#define xS (OF_RES2 | OF_STR2) +#define Vx OF_RES1 +#define VV (OF_RES1 | OF_RES2) +#define Nx (OF_RES1 | OF_NUM1) +#define NV (OF_RES1 | OF_NUM1 | OF_RES2) +#define Sx (OF_RES1 | OF_STR1) +#define SV (OF_RES1 | OF_STR1 | OF_RES2) +#define SS (OF_RES1 | OF_STR1 | OF_RES2 | OF_STR2) + +#define OPCLSMASK 0xFF00 +#define OPNMASK 0x007F + +/* operator priority is a highest byte (even: r->l, odd: l->r grouping) + * For builtins it has different meaning: n n s3 s2 s1 v3 v2 v1, + * n - min. number of args, vN - resolve Nth arg to var, sN - resolve to string + */ +#undef P +#undef PRIMASK +#undef PRIMASK2 +#define P(x) (x << 24) +#define PRIMASK 0x7F000000 +#define PRIMASK2 0x7E000000 + +/* Operation classes */ + +#define SHIFT_TIL_THIS 0x0600 +#define RECUR_FROM_THIS 0x1000 + +enum { + OC_DELETE = 0x0100, OC_EXEC = 0x0200, OC_NEWSOURCE = 0x0300, + OC_PRINT = 0x0400, OC_PRINTF = 0x0500, OC_WALKINIT = 0x0600, + + OC_BR = 0x0700, OC_BREAK = 0x0800, OC_CONTINUE = 0x0900, + OC_EXIT = 0x0a00, OC_NEXT = 0x0b00, OC_NEXTFILE = 0x0c00, + OC_TEST = 0x0d00, OC_WALKNEXT = 0x0e00, + + OC_BINARY = 0x1000, OC_BUILTIN = 0x1100, OC_COLON = 0x1200, + OC_COMMA = 0x1300, OC_COMPARE = 0x1400, OC_CONCAT = 0x1500, + OC_FBLTIN = 0x1600, OC_FIELD = 0x1700, OC_FNARG = 0x1800, + OC_FUNC = 0x1900, OC_GETLINE = 0x1a00, OC_IN = 0x1b00, + OC_LAND = 0x1c00, OC_LOR = 0x1d00, OC_MATCH = 0x1e00, + OC_MOVE = 0x1f00, OC_PGETLINE = 0x2000, OC_REGEXP = 0x2100, + OC_REPLACE = 0x2200, OC_RETURN = 0x2300, OC_SPRINTF = 0x2400, + OC_TERNARY = 0x2500, OC_UNARY = 0x2600, OC_VAR = 0x2700, + OC_DONE = 0x2800, + + ST_IF = 0x3000, ST_DO = 0x3100, ST_FOR = 0x3200, + ST_WHILE = 0x3300 +}; + +/* simple builtins */ +enum { + F_in, F_rn, F_co, F_ex, F_lg, F_si, F_sq, F_sr, + F_ti, F_le, F_sy, F_ff, F_cl +}; + +/* builtins */ +enum { + B_a2, B_ix, B_ma, B_sp, B_ss, B_ti, B_mt, B_lo, B_up, + B_ge, B_gs, B_su, + B_an, B_co, B_ls, B_or, B_rs, B_xo, +}; + +/* tokens and their corresponding info values */ + +#define NTC "\377" /* switch to next token class (tc<<1) */ +#define NTCC '\377' + +static const char tokenlist[] ALIGN1 = + "\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 /* 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 /* TC_WHILE */ + "\4else" NTC /* TC_ELSE */ + "\3and" "\5compl" "\6lshift" "\2or" /* TC_BUILTIN */ + "\6rshift" "\3xor" + "\5close" "\6system" "\6fflush" "\5atan2" + "\3cos" "\3exp" "\3int" "\3log" + "\4rand" "\3sin" "\4sqrt" "\5srand" + "\6gensub" "\4gsub" "\5index" /* "\6length" was here */ + "\5match" "\5split" "\7sprintf" "\3sub" + "\6substr" "\7systime" "\10strftime" "\6mktime" + "\7tolower" "\7toupper" NTC + "\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, + OC_REGEXP, + xS|'a', xS|'w', xS|'|', + OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m', + OC_UNARY|xV|P(9)|'P', OC_UNARY|xV|P(9)|'M', OC_FIELD|xV|P(5), + OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-', + OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&', + OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&', + OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*', + 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), /* 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)|'!', + 0, /* ] */ + 0, + 0, + 0, + 0, /* \n */ + ST_IF, ST_DO, ST_FOR, OC_BREAK, + OC_CONTINUE, OC_DELETE|Vx, OC_PRINT, + OC_PRINTF, OC_NEXT, OC_NEXTFILE, + 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, 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 /* TC_END */ +}; + +/* internal variable names and their initial values */ +/* asterisk marks SPECIAL vars; $ is just no-named Field0 */ +enum { + CONVFMT, OFMT, FS, OFS, + ORS, RS, RT, FILENAME, + SUBSEP, F0, ARGIND, ARGC, + ARGV, ERRNO, FNR, NR, + NF, IGNORECASE, ENVIRON, NUM_INTERNAL_VARS +}; + +static const char vNames[] ALIGN1 = + "CONVFMT\0" "OFMT\0" "FS\0*" "OFS\0" + "ORS\0" "RS\0*" "RT\0" "FILENAME\0" + "SUBSEP\0" "$\0*" "ARGIND\0" "ARGC\0" + "ARGV\0" "ERRNO\0" "FNR\0" "NR\0" + "NF\0*" "IGNORECASE\0*" "ENVIRON\0" "\0"; + +static const char vValues[] ALIGN1 = + "%.6g\0" "%.6g\0" " \0" " \0" + "\n\0" "\n\0" "\0" "\0" + "\034\0" "\0" "\377"; + +/* hash size may grow to these values */ +#define FIRST_PRIME 61 +static const uint16_t PRIMES[] ALIGN2 = { 251, 1021, 4093, 16381, 65521 }; + + +/* Globals. Split in two parts so that first one is addressed + * with (mostly short) negative offsets. + * NB: it's unsafe to put members of type "double" + * into globals2 (gcc may fail to align them). + */ +struct globals { + double t_double; + chain beginseq, mainseq, endseq; + chain *seq; + node *break_ptr, *continue_ptr; + rstream *iF; + xhash *vhash, *ahash, *fdhash, *fnhash; + const char *g_progname; + int g_lineno; + int nfields; + int maxfields; /* used in fsrealloc() only */ + var *Fields; + nvblock *g_cb; + char *g_pos; + char *g_buf; + smallint icase; + smallint exiting; + smallint nextrec; + smallint nextfile; + smallint is_f0_split; + smallint t_rollback; +}; +struct globals2 { + uint32_t t_info; /* often used */ + uint32_t t_tclass; + char *t_string; + int t_lineno; + + var *intvar[NUM_INTERNAL_VARS]; /* often used */ + + /* former statics from various functions */ + char *split_f0__fstrings; + + uint32_t next_token__save_tclass; + uint32_t next_token__save_info; + uint32_t next_token__ltclass; + smallint next_token__concat_inserted; + + smallint next_input_file__files_happen; + rstream next_input_file__rsm; + + var *evaluate__fnargs; + unsigned evaluate__seed; + regex_t evaluate__sreg; + + var ptest__v; + + tsplitter exec_builtin__tspl; + + /* biggest and least used members go last */ + tsplitter fsplitter, rsplitter; +}; +#define G1 (ptr_to_globals[-1]) +#define G (*(struct globals2 *)ptr_to_globals) +/* For debug. nm --size-sort awk.o | grep -vi ' [tr] ' */ +/*char G1size[sizeof(G1)]; - 0x74 */ +/*char Gsize[sizeof(G)]; - 0x1c4 */ +/* Trying to keep most of members accessible with short offsets: */ +/*char Gofs_seed[offsetof(struct globals2, evaluate__seed)]; - 0x90 */ +#define t_double (G1.t_double ) +#define beginseq (G1.beginseq ) +#define mainseq (G1.mainseq ) +#define endseq (G1.endseq ) +#define seq (G1.seq ) +#define break_ptr (G1.break_ptr ) +#define continue_ptr (G1.continue_ptr) +#define iF (G1.iF ) +#define vhash (G1.vhash ) +#define ahash (G1.ahash ) +#define fdhash (G1.fdhash ) +#define fnhash (G1.fnhash ) +#define g_progname (G1.g_progname ) +#define g_lineno (G1.g_lineno ) +#define nfields (G1.nfields ) +#define maxfields (G1.maxfields ) +#define Fields (G1.Fields ) +#define g_cb (G1.g_cb ) +#define g_pos (G1.g_pos ) +#define g_buf (G1.g_buf ) +#define icase (G1.icase ) +#define exiting (G1.exiting ) +#define nextrec (G1.nextrec ) +#define nextfile (G1.nextfile ) +#define is_f0_split (G1.is_f0_split ) +#define t_rollback (G1.t_rollback ) +#define t_info (G.t_info ) +#define t_tclass (G.t_tclass ) +#define t_string (G.t_string ) +#define t_lineno (G.t_lineno ) +#define intvar (G.intvar ) +#define fsplitter (G.fsplitter ) +#define rsplitter (G.rsplitter ) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS((char*)xzalloc(sizeof(G1)+sizeof(G)) + sizeof(G1)); \ + G.next_token__ltclass = TC_OPTERM; \ + G.evaluate__seed = 1; \ +} while (0) + + +/* function prototypes */ +static void handle_special(var *); +static node *parse_expr(uint32_t); +static void chain_group(void); +static var *evaluate(node *, var *); +static rstream *next_input_file(void); +static int fmt_num(char *, int, const char *, double, int); +static int awk_exit(int) NORETURN; + +/* ---- error handling ---- */ + +static const char EMSG_INTERNAL_ERROR[] ALIGN1 = "Internal error"; +static const char EMSG_UNEXP_EOS[] ALIGN1 = "Unexpected end of string"; +static const char EMSG_UNEXP_TOKEN[] ALIGN1 = "Unexpected token"; +static const char EMSG_DIV_BY_ZERO[] ALIGN1 = "Division by zero"; +static const char EMSG_INV_FMT[] ALIGN1 = "Invalid format specifier"; +static const char EMSG_TOO_FEW_ARGS[] ALIGN1 = "Too few arguments for builtin"; +static const char EMSG_NOT_ARRAY[] ALIGN1 = "Not an array"; +static const char EMSG_POSSIBLE_ERROR[] ALIGN1 = "Possible syntax error"; +static const char EMSG_UNDEF_FUNC[] ALIGN1 = "Call to undefined function"; +static const char EMSG_NO_MATH[] ALIGN1 = "Math support is not compiled in"; +static const char EMSG_NEGATIVE_FIELD[] ALIGN1 = "Access to negative field"; + +static void zero_out_var(var *vp) +{ + memset(vp, 0, sizeof(*vp)); +} + +static void syntax_error(const char *message) NORETURN; +static void syntax_error(const char *message) +{ + bb_error_msg_and_die("%s:%i: %s", g_progname, g_lineno, message); +} + +/* ---- hash stuff ---- */ + +static unsigned hashidx(const char *name) +{ + unsigned idx = 0; + + while (*name) + idx = *name++ + (idx << 6) - idx; + return idx; +} + +/* create new hash */ +static xhash *hash_init(void) +{ + xhash *newhash; + + newhash = xzalloc(sizeof(*newhash)); + newhash->csize = FIRST_PRIME; + newhash->items = xzalloc(FIRST_PRIME * sizeof(newhash->items[0])); + + return newhash; +} + +/* find item in hash, return ptr to data, NULL if not found */ +static void *hash_search(xhash *hash, const char *name) +{ + hash_item *hi; + + hi = hash->items[hashidx(name) % hash->csize]; + while (hi) { + if (strcmp(hi->name, name) == 0) + return &hi->data; + hi = hi->next; + } + return NULL; +} + +/* grow hash if it becomes too big */ +static void hash_rebuild(xhash *hash) +{ + unsigned newsize, i, idx; + hash_item **newitems, *hi, *thi; + + if (hash->nprime == ARRAY_SIZE(PRIMES)) + return; + + newsize = PRIMES[hash->nprime++]; + newitems = xzalloc(newsize * sizeof(newitems[0])); + + for (i = 0; i < hash->csize; i++) { + hi = hash->items[i]; + while (hi) { + thi = hi; + hi = thi->next; + idx = hashidx(thi->name) % newsize; + thi->next = newitems[idx]; + newitems[idx] = thi; + } + } + + free(hash->items); + hash->csize = newsize; + hash->items = newitems; +} + +/* find item in hash, add it if necessary. Return ptr to data */ +static void *hash_find(xhash *hash, const char *name) +{ + hash_item *hi; + unsigned idx; + int l; + + hi = hash_search(hash, name); + if (!hi) { + if (++hash->nel / hash->csize > 10) + hash_rebuild(hash); + + l = strlen(name) + 1; + hi = xzalloc(sizeof(*hi) + l); + strcpy(hi->name, name); + + idx = hashidx(name) % hash->csize; + hi->next = hash->items[idx]; + hash->items[idx] = hi; + hash->glen += l; + } + return &hi->data; +} + +#define findvar(hash, name) ((var*) hash_find((hash), (name))) +#define newvar(name) ((var*) hash_find(vhash, (name))) +#define newfile(name) ((rstream*)hash_find(fdhash, (name))) +#define newfunc(name) ((func*) hash_find(fnhash, (name))) + +static void hash_remove(xhash *hash, const char *name) +{ + hash_item *hi, **phi; + + phi = &hash->items[hashidx(name) % hash->csize]; + while (*phi) { + hi = *phi; + if (strcmp(hi->name, name) == 0) { + hash->glen -= (strlen(name) + 1); + hash->nel--; + *phi = hi->next; + free(hi); + break; + } + phi = &hi->next; + } +} + +/* ------ some useful functions ------ */ + +static char *skip_spaces(char *p) +{ + while (1) { + if (*p == '\\' && p[1] == '\n') { + p++; + t_lineno++; + } else if (*p != ' ' && *p != '\t') { + break; + } + p++; + } + return p; +} + +/* returns old *s, advances *s past word and terminating NUL */ +static char *nextword(char **s) +{ + char *p = *s; + while (*(*s)++ != '\0') + continue; + return p; +} + +static char nextchar(char **s) +{ + char c, *pps; + + c = *(*s)++; + pps = *s; + if (c == '\\') + c = bb_process_escape_sequence((const char**)s); + /* Example awk statement: + * s = "abc\"def" + * we must treat \" as " + */ + if (c == '\\' && *s == pps) { /* unrecognized \z? */ + c = *(*s); /* yes, fetch z */ + if (c) + (*s)++; /* advance unless z = NUL */ + } + return c; +} + +/* TODO: merge with strcpy_and_process_escape_sequences()? + */ +static void unescape_string_in_place(char *s1) +{ + char *s = s1; + while ((*s1 = nextchar(&s)) != '\0') + s1++; +} + +static ALWAYS_INLINE int isalnum_(int c) +{ + return (isalnum(c) || c == '_'); +} + +static double my_strtod(char **pp) +{ + char *cp = *pp; + if (ENABLE_DESKTOP && cp[0] == '0') { + /* Might be hex or octal integer: 0x123abc or 07777 */ + char c = (cp[1] | 0x20); + if (c == 'x' || isdigit(cp[1])) { + unsigned long long ull = strtoull(cp, pp, 0); + if (c == 'x') + return ull; + c = **pp; + if (!isdigit(c) && c != '.') + return ull; + /* else: it may be a floating number. Examples: + * 009.123 (*pp points to '9') + * 000.123 (*pp points to '.') + * fall through to strtod. + */ + } + } + return strtod(cp, pp); +} + +/* -------- working with variables (set/get/copy/etc) -------- */ + +static xhash *iamarray(var *v) +{ + var *a = v; + + while (a->type & VF_CHILD) + a = a->x.parent; + + if (!(a->type & VF_ARRAY)) { + a->type |= VF_ARRAY; + a->x.array = hash_init(); + } + return a->x.array; +} + +static void clear_array(xhash *array) +{ + unsigned i; + hash_item *hi, *thi; + + for (i = 0; i < array->csize; i++) { + hi = array->items[i]; + while (hi) { + thi = hi; + hi = hi->next; + free(thi->data.v.string); + free(thi); + } + array->items[i] = NULL; + } + array->glen = array->nel = 0; +} + +/* clear a variable */ +static var *clrvar(var *v) +{ + if (!(v->type & VF_FSTR)) + free(v->string); + + v->type &= VF_DONTTOUCH; + v->type |= VF_DIRTY; + v->string = NULL; + return v; +} + +/* assign string value to variable */ +static var *setvar_p(var *v, char *value) +{ + clrvar(v); + v->string = value; + handle_special(v); + return v; +} + +/* same as setvar_p but make a copy of string */ +static var *setvar_s(var *v, const char *value) +{ + return setvar_p(v, (value && *value) ? xstrdup(value) : NULL); +} + +/* same as setvar_s but sets USER flag */ +static var *setvar_u(var *v, const char *value) +{ + v = setvar_s(v, value); + v->type |= VF_USER; + return v; +} + +/* set array element to user string */ +static void setari_u(var *a, int idx, const char *s) +{ + var *v; + + v = findvar(iamarray(a), itoa(idx)); + setvar_u(v, s); +} + +/* assign numeric value to variable */ +static var *setvar_i(var *v, double value) +{ + clrvar(v); + v->type |= VF_NUMBER; + v->number = value; + handle_special(v); + return v; +} + +static const char *getvar_s(var *v) +{ + /* if v is numeric and has no cached string, convert it to string */ + if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) { + fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[CONVFMT]), v->number, TRUE); + v->string = xstrdup(g_buf); + v->type |= VF_CACHED; + } + return (v->string == NULL) ? "" : v->string; +} + +static double getvar_i(var *v) +{ + char *s; + + if ((v->type & (VF_NUMBER | VF_CACHED)) == 0) { + v->number = 0; + s = v->string; + if (s && *s) { + debug_printf_eval("getvar_i: '%s'->", s); + v->number = my_strtod(&s); + debug_printf_eval("%f (s:'%s')\n", v->number, s); + if (v->type & VF_USER) { + s = skip_spaces(s); + if (*s != '\0') + v->type &= ~VF_USER; + } + } else { + debug_printf_eval("getvar_i: '%s'->zero\n", s); + v->type &= ~VF_USER; + } + v->type |= VF_CACHED; + } + debug_printf_eval("getvar_i: %f\n", v->number); + return v->number; +} + +/* Used for operands of bitwise ops */ +static unsigned long getvar_i_int(var *v) +{ + double d = getvar_i(v); + + /* Casting doubles to longs is undefined for values outside + * of target type range. Try to widen it as much as possible */ + if (d >= 0) + return (unsigned long)d; + /* Why? Think about d == -4294967295.0 (assuming 32bit longs) */ + return - (long) (unsigned long) (-d); +} + +static var *copyvar(var *dest, const var *src) +{ + if (dest != src) { + clrvar(dest); + dest->type |= (src->type & ~(VF_DONTTOUCH | VF_FSTR)); + debug_printf_eval("copyvar: number:%f string:'%s'\n", src->number, src->string); + dest->number = src->number; + if (src->string) + dest->string = xstrdup(src->string); + } + handle_special(dest); + return dest; +} + +static var *incvar(var *v) +{ + return setvar_i(v, getvar_i(v) + 1.0); +} + +/* return true if v is number or numeric string */ +static int is_numeric(var *v) +{ + getvar_i(v); + return ((v->type ^ VF_DIRTY) & (VF_NUMBER | VF_USER | VF_DIRTY)); +} + +/* return 1 when value of v corresponds to true, 0 otherwise */ +static int istrue(var *v) +{ + if (is_numeric(v)) + return (v->number != 0); + return (v->string && v->string[0]); +} + +/* temporary variables allocator. Last allocated should be first freed */ +static var *nvalloc(int n) +{ + nvblock *pb = NULL; + var *v, *r; + int size; + + while (g_cb) { + pb = g_cb; + if ((g_cb->pos - g_cb->nv) + n <= g_cb->size) + break; + g_cb = g_cb->next; + } + + if (!g_cb) { + size = (n <= MINNVBLOCK) ? MINNVBLOCK : n; + g_cb = xzalloc(sizeof(nvblock) + size * sizeof(var)); + g_cb->size = size; + g_cb->pos = g_cb->nv; + g_cb->prev = pb; + /*g_cb->next = NULL; - xzalloc did it */ + if (pb) + pb->next = g_cb; + } + + v = r = g_cb->pos; + g_cb->pos += n; + + while (v < g_cb->pos) { + v->type = 0; + v->string = NULL; + v++; + } + + return r; +} + +static void nvfree(var *v) +{ + var *p; + + if (v < g_cb->nv || v >= g_cb->pos) + syntax_error(EMSG_INTERNAL_ERROR); + + for (p = v; p < g_cb->pos; p++) { + if ((p->type & (VF_ARRAY | VF_CHILD)) == VF_ARRAY) { + clear_array(iamarray(p)); + free(p->x.array->items); + free(p->x.array); + } + if (p->type & VF_WALK) { + walker_list *n; + walker_list *w = p->x.walker; + debug_printf_walker("nvfree: freeing walker @%p\n", &p->x.walker); + p->x.walker = NULL; + while (w) { + n = w->prev; + debug_printf_walker(" free(%p)\n", w); + free(w); + w = n; + } + } + clrvar(p); + } + + g_cb->pos = v; + while (g_cb->prev && g_cb->pos == g_cb->nv) { + g_cb = g_cb->prev; + } +} + +/* ------- awk program text parsing ------- */ + +/* Parse next token pointed by global pos, place results into global ttt. + * If token isn't expected, give away. Return token class + */ +static uint32_t next_token(uint32_t expected) +{ +#define concat_inserted (G.next_token__concat_inserted) +#define save_tclass (G.next_token__save_tclass) +#define save_info (G.next_token__save_info) +/* Initialized to TC_OPTERM: */ +#define ltclass (G.next_token__ltclass) + + char *p, *s; + const char *tl; + uint32_t tc; + const uint32_t *ti; + + 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: + p = skip_spaces(p); + g_lineno = t_lineno; + if (*p == '#') + while (*p != '\n' && *p != '\0') + p++; + + if (*p == '\n') + t_lineno++; + + 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; + while (*p != '\"') { + char *pp; + if (*p == '\0' || *p == '\n') + syntax_error(EMSG_UNEXP_EOS); + pp = p; + *s++ = nextchar(&pp); + p = pp; + } + p++; + *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; + while (*p != '/') { + if (*p == '\0' || *p == '\n') + syntax_error(EMSG_UNEXP_EOS); + *s = *p++; + if (*s++ == '\\') { + char *pp = p; + s[-1] = bb_process_escape_sequence((const char **)&pp); + if (*p == '\\') + *s++ = '\\'; + if (pp == p) + *s++ = *p++; + else + p = pp; + } + } + p++; + *s = '\0'; + tc = TC_REGEXP; + debug_printf_parse("%s: token found:'%s' TC_REGEXP\n", __func__, t_string); + + } else if (*p == '.' || isdigit(*p)) { + /* it's a number */ + char *pp = p; + t_double = my_strtod(&pp); + p = pp; + if (*p == '.') + 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; + tc = 0x00000001; + ti = tokeninfo; + while (*tl) { + int l = (unsigned char) *tl++; + if (l == (unsigned char) NTCC) { + tc <<= 1; + continue; + } + /* if token class is expected, + * token matches, + * and it's not a longer word, + */ + if ((tc & (expected | TC_WORD | TC_NEWLINE)) + && strncmp(p, tl, l) == 0 + && !((tc & TC_WORD) && isalnum_(p[l])) + ) { + /* then this is what we are looking for */ + t_info = *ti; + debug_printf_parse("%s: token found:'%.*s' t_info:%x\n", __func__, l, p, t_info); + p += l; + goto token_found; + } + ti++; + tl += l; + } + /* not a known token */ + + /* is it a name? (var/array/function) */ + if (!isalnum_(*p)) + syntax_error(EMSG_UNEXP_TOKEN); /* no */ + /* yes */ + t_string = --p; + while (isalnum_(*++p)) { + p[-1] = *p; + } + p[-1] = '\0'; + tc = TC_VARIABLE; + /* also consume whitespace between functionname and bracket */ + if (!(expected & TC_VARIABLE) || (expected & TC_ARRAY)) + p = skip_spaces(p); + if (*p == '(') { + tc = TC_FUNCTION; + debug_printf_parse("%s: token found:'%s' TC_FUNCTION\n", __func__, t_string); + } else { + if (*p == '[') { + p++; + tc = TC_ARRAY; + debug_printf_parse("%s: token found:'%s' TC_ARRAY\n", __func__, t_string); + } else + debug_printf_parse("%s: token found:'%s' TC_VARIABLE\n", __func__, t_string); + } + } + token_found: + g_pos = p; + + /* skipping newlines in some cases */ + if ((ltclass & TC_NOTERM) && (tc & TC_NEWLINE)) + goto readnext; + + /* insert concatenation operator when needed */ + if ((ltclass & TC_CONCAT1) && (tc & TC_CONCAT2) && (expected & TC_BINOP)) { + concat_inserted = TRUE; + save_tclass = tc; + save_info = t_info; + tc = TC_BINOP; + t_info = OC_CONCAT | SS | P(35); + } + + t_tclass = tc; + } + ltclass = t_tclass; + + /* Are we ready for this? */ + if (!(ltclass & expected)) { + syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ? + EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN); + } + + return ltclass; +#undef concat_inserted +#undef save_tclass +#undef save_info +#undef ltclass +} + +static void rollback_token(void) +{ + t_rollback = TRUE; +} + +static node *new_node(uint32_t info) +{ + node *n; + + n = xzalloc(sizeof(node)); + n->info = info; + n->lineno = g_lineno; + return n; +} + +static void mk_re_node(const char *s, node *n, regex_t *re) +{ + n->info = OC_REGEXP; + n->l.re = re; + n->r.ire = re + 1; + xregcomp(re, s, REG_EXTENDED); + xregcomp(re + 1, s, REG_EXTENDED | REG_ICASE); +} + +static node *condition(void) +{ + next_token(TC_SEQSTART); + return parse_expr(TC_SEQTERM); +} + +/* parse expression terminated by given argument, return ptr + * to built subtree. Terminator is eaten by parse_expr */ +static node *parse_expr(uint32_t iexp) +{ + node sn; + node *cn = &sn; + node *vn, *glptr; + uint32_t tc, xtc; + var *v; + + debug_printf_parse("%s(%x)\n", __func__, iexp); + + sn.info = PRIMASK; + sn.r.n = glptr = NULL; + xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | iexp; + + while (!((tc = next_token(xtc)) & iexp)) { + + if (glptr && (t_info == (OC_COMPARE | VV | P(39) | 2))) { + /* input redirection (<) attached to glptr node */ + debug_printf_parse("%s: input redir\n", __func__); + cn = glptr->l.n = new_node(OC_CONCAT | SS | P(37)); + cn->a.n = glptr; + xtc = TC_OPERAND | TC_UOPPRE; + glptr = NULL; + + } else if (tc & (TC_BINOP | TC_UOPPOST)) { + debug_printf_parse("%s: TC_BINOP | TC_UOPPOST\n", __func__); + /* for binary and postfix-unary operators, jump back over + * previous operators with higher priority */ + vn = cn; + while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2)) + || ((t_info == vn->info) && ((t_info & OPCLSMASK) == OC_COLON)) + ) { + vn = vn->a.n; + } + if ((t_info & OPCLSMASK) == OC_TERNARY) + t_info += P(6); + cn = vn->a.n->r.n = new_node(t_info); + cn->a.n = vn->a.n; + if (tc & TC_BINOP) { + cn->l.n = vn; + xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP; + if ((t_info & OPCLSMASK) == OC_PGETLINE) { + /* it's a pipe */ + next_token(TC_GETLINE); + /* give maximum priority to this pipe */ + cn->info &= ~PRIMASK; + xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; + } + } else { + cn->r.n = vn; + xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; + } + vn->a.n = cn; + + } else { + debug_printf_parse("%s: other\n", __func__); + /* for operands and prefix-unary operators, attach them + * to last node */ + vn = cn; + cn = vn->r.n = new_node(t_info); + cn->a.n = vn; + xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP; + if (tc & (TC_OPERAND | TC_REGEXP)) { + debug_printf_parse("%s: TC_OPERAND | TC_REGEXP\n", __func__); + xtc = TC_UOPPRE | TC_UOPPOST | TC_BINOP | TC_OPERAND | iexp; + /* one should be very careful with switch on tclass - + * only simple tclasses should be used! */ + switch (tc) { + case TC_VARIABLE: + case TC_ARRAY: + debug_printf_parse("%s: TC_VARIABLE | TC_ARRAY\n", __func__); + cn->info = OC_VAR; + v = hash_search(ahash, t_string); + if (v != NULL) { + cn->info = OC_FNARG; + cn->l.aidx = v->x.aidx; + } else { + cn->l.v = newvar(t_string); + } + if (tc & TC_ARRAY) { + cn->info |= xS; + cn->r.n = parse_expr(TC_ARRTERM); + } + break; + + case TC_NUMBER: + case TC_STRING: + debug_printf_parse("%s: TC_NUMBER | TC_STRING\n", __func__); + cn->info = OC_VAR; + v = cn->l.v = xzalloc(sizeof(var)); + if (tc & TC_NUMBER) + setvar_i(v, t_double); + else + setvar_s(v, t_string); + break; + + case TC_REGEXP: + debug_printf_parse("%s: TC_REGEXP\n", __func__); + mk_re_node(t_string, cn, xzalloc(sizeof(regex_t)*2)); + break; + + case TC_FUNCTION: + debug_printf_parse("%s: TC_FUNCTION\n", __func__); + cn->info = OC_FUNC; + cn->r.f = newfunc(t_string); + cn->l.n = condition(); + break; + + case TC_SEQSTART: + debug_printf_parse("%s: TC_SEQSTART\n", __func__); + cn = vn->r.n = parse_expr(TC_SEQTERM); + if (!cn) + syntax_error("Empty sequence"); + cn->a.n = vn; + break; + + case TC_GETLINE: + debug_printf_parse("%s: TC_GETLINE\n", __func__); + glptr = cn; + xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp; + break; + + case TC_BUILTIN: + 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; + } + } + } + } + + debug_printf_parse("%s() returns %p\n", __func__, sn.r.n); + return sn.r.n; +} + +/* add node to chain. Return ptr to alloc'd node */ +static node *chain_node(uint32_t info) +{ + node *n; + + if (!seq->first) + seq->first = seq->last = new_node(0); + + if (seq->programname != g_progname) { + seq->programname = g_progname; + n = chain_node(OC_NEWSOURCE); + n->l.new_progname = xstrdup(g_progname); + } + + n = seq->last; + n->info = info; + seq->last = n->a.n = new_node(OC_DONE); + + return n; +} + +static void chain_expr(uint32_t info) +{ + node *n; + + n = chain_node(info); + n->l.n = parse_expr(TC_OPTERM | TC_GRPTERM); + if (t_tclass & TC_GRPTERM) + rollback_token(); +} + +static node *chain_loop(node *nn) +{ + node *n, *n2, *save_brk, *save_cont; + + save_brk = break_ptr; + save_cont = continue_ptr; + + n = chain_node(OC_BR | Vx); + continue_ptr = new_node(OC_EXEC); + break_ptr = new_node(OC_EXEC); + chain_group(); + n2 = chain_node(OC_EXEC | Vx); + n2->l.n = nn; + n2->a.n = n; + continue_ptr->a.n = n2; + break_ptr->a.n = n->r.n = seq->last; + + continue_ptr = save_cont; + break_ptr = save_brk; + + return n; +} + +/* parse group and attach it to chain */ +static void chain_group(void) +{ + uint32_t c; + node *n, *n2, *n3; + + do { + c = next_token(TC_GRPSEQ); + } while (c & TC_NEWLINE); + + if (c & TC_GRPSTART) { + debug_printf_parse("%s: TC_GRPSTART\n", __func__); + while (next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) { + debug_printf_parse("%s: !TC_GRPTERM\n", __func__); + if (t_tclass & TC_NEWLINE) + continue; + rollback_token(); + chain_group(); + } + debug_printf_parse("%s: TC_GRPTERM\n", __func__); + } else if (c & (TC_OPSEQ | TC_OPTERM)) { + debug_printf_parse("%s: TC_OPSEQ | TC_OPTERM\n", __func__); + rollback_token(); + chain_expr(OC_EXEC | Vx); + } else { + /* TC_STATEMNT */ + debug_printf_parse("%s: TC_STATEMNT(?)\n", __func__); + switch (t_info & OPCLSMASK) { + case ST_IF: + debug_printf_parse("%s: ST_IF\n", __func__); + n = chain_node(OC_BR | Vx); + n->l.n = condition(); + chain_group(); + n2 = chain_node(OC_EXEC); + n->r.n = seq->last; + if (next_token(TC_GRPSEQ | TC_GRPTERM | TC_ELSE) == TC_ELSE) { + chain_group(); + n2->a.n = seq->last; + } else { + rollback_token(); + } + break; + + case ST_WHILE: + debug_printf_parse("%s: ST_WHILE\n", __func__); + n2 = condition(); + n = chain_loop(NULL); + n->l.n = n2; + break; + + case ST_DO: + debug_printf_parse("%s: ST_DO\n", __func__); + n2 = chain_node(OC_EXEC); + n = chain_loop(NULL); + n2->a.n = n->a.n; + next_token(TC_WHILE); + n->l.n = condition(); + break; + + case ST_FOR: + debug_printf_parse("%s: ST_FOR\n", __func__); + next_token(TC_SEQSTART); + n2 = parse_expr(TC_SEMICOL | TC_SEQTERM); + if (t_tclass & TC_SEQTERM) { /* for-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; + n->r.n = n2->r.n; + n = chain_loop(NULL); + n->info = OC_WALKNEXT | Vx; + n->l.n = n2->l.n; + } else { /* for (;;) */ + n = chain_node(OC_EXEC | Vx); + n->l.n = n2; + n2 = parse_expr(TC_SEMICOL); + n3 = parse_expr(TC_SEQTERM); + n = chain_loop(n3); + n->l.n = n2; + if (!n2) + n->info = OC_EXEC; + } + break; + + case OC_PRINT: + case OC_PRINTF: + debug_printf_parse("%s: OC_PRINT[F]\n", __func__); + n = chain_node(t_info); + n->l.n = parse_expr(TC_OPTERM | TC_OUTRDR | TC_GRPTERM); + if (t_tclass & TC_OUTRDR) { + n->info |= t_info; + n->r.n = parse_expr(TC_OPTERM | TC_GRPTERM); + } + if (t_tclass & TC_GRPTERM) + rollback_token(); + break; + + case OC_BREAK: + 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 */ + default: + debug_printf_parse("%s: default\n", __func__); + chain_expr(t_info); + } + } +} + +static void parse_program(char *p) +{ + uint32_t tclass; + node *cn; + func *f; + var *v; + + g_pos = p; + t_lineno = 1; + while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART | + TC_OPTERM | TC_BEGIN | TC_END | TC_FUNCDECL)) != TC_EOF) { + + if (tclass & TC_OPTERM) { + debug_printf_parse("%s: TC_OPTERM\n", __func__); + continue; + } + + seq = &mainseq; + if (tclass & TC_BEGIN) { + 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); + g_pos++; + f = newfunc(t_string); + f->body.first = NULL; + f->nargs = 0; + while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) { + v = findvar(ahash, t_string); + v->x.aidx = f->nargs++; + + if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM) + break; + } + seq = &f->body; + chain_group(); + clear_array(ahash); + } else if (tclass & TC_OPSEQ) { + debug_printf_parse("%s: TC_OPSEQ\n", __func__); + rollback_token(); + cn = chain_node(OC_TEST); + cn->l.n = parse_expr(TC_OPTERM | TC_EOF | TC_GRPSTART); + if (t_tclass & TC_GRPSTART) { + debug_printf_parse("%s: TC_GRPSTART\n", __func__); + rollback_token(); + chain_group(); + } else { + debug_printf_parse("%s: !TC_GRPSTART\n", __func__); + chain_node(OC_PRINT); + } + cn->r.n = mainseq.last; + } else /* if (tclass & TC_GRPSTART) */ { + debug_printf_parse("%s: TC_GRPSTART(?)\n", __func__); + rollback_token(); + chain_group(); + } + } + debug_printf_parse("%s: TC_EOF\n", __func__); +} + + +/* -------- program execution part -------- */ + +static node *mk_splitter(const char *s, tsplitter *spl) +{ + regex_t *re, *ire; + node *n; + + re = &spl->re[0]; + ire = &spl->re[1]; + n = &spl->n; + if ((n->info & OPCLSMASK) == OC_REGEXP) { + regfree(re); + regfree(ire); // TODO: nuke ire, use re+1? + } + if (s[0] && s[1]) { /* strlen(s) > 1 */ + mk_re_node(s, n, re); + } else { + n->info = (uint32_t) s[0]; + } + + return n; +} + +/* use node as a regular expression. Supplied with node ptr and regex_t + * storage space. Return ptr to regex (if result points to preg, it should + * be later regfree'd manually + */ +static regex_t *as_regex(node *op, regex_t *preg) +{ + int cflags; + var *v; + const char *s; + + if ((op->info & OPCLSMASK) == OC_REGEXP) { + return icase ? op->r.ire : op->l.re; + } + v = nvalloc(1); + s = getvar_s(evaluate(op, v)); + + cflags = icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED; + /* Testcase where REG_EXTENDED fails (unpaired '{'): + * echo Hi | awk 'gsub("@(samp|code|file)\{","");' + * gawk 3.1.5 eats this. We revert to ~REG_EXTENDED + * (maybe gsub is not supposed to use REG_EXTENDED?). + */ + if (regcomp(preg, s, cflags)) { + cflags &= ~REG_EXTENDED; + xregcomp(preg, s, cflags); + } + nvfree(v); + return preg; +} + +/* gradually increasing buffer. + * note that we reallocate even if n == old_size, + * and thus there is at least one extra allocated byte. + */ +static char* qrealloc(char *b, int n, int *size) +{ + if (!b || n >= *size) { + *size = n + (n>>1) + 80; + b = xrealloc(b, *size); + } + return b; +} + +/* resize field storage space */ +static void fsrealloc(int size) +{ + int i; + + if (size >= maxfields) { + i = maxfields; + maxfields = size + 16; + Fields = xrealloc(Fields, maxfields * sizeof(Fields[0])); + for (; i < maxfields; i++) { + Fields[i].type = VF_SPECIAL; + Fields[i].string = NULL; + } + } + /* if size < nfields, clear extra field variables */ + for (i = size; i < nfields; i++) { + clrvar(Fields + i); + } + nfields = size; +} + +static int awk_split(const char *s, node *spl, char **slist) +{ + int l, n; + char c[4]; + char *s1; + regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough... + + /* in worst case, each char would be a separate field */ + *slist = s1 = xzalloc(strlen(s) * 2 + 3); + strcpy(s1, s); + + c[0] = c[1] = (char)spl->info; + c[2] = c[3] = '\0'; + if (*getvar_s(intvar[RS]) == '\0') + c[2] = '\n'; + + n = 0; + if ((spl->info & OPCLSMASK) == OC_REGEXP) { /* regex split */ + if (!*s) + return n; /* "": zero fields */ + n++; /* at least one field will be there */ + do { + l = strcspn(s, c+2); /* len till next NUL or \n */ + if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0 + && pmatch[0].rm_so <= l + ) { + l = pmatch[0].rm_so; + if (pmatch[0].rm_eo == 0) { + l++; + pmatch[0].rm_eo++; + } + n++; /* we saw yet another delimiter */ + } else { + pmatch[0].rm_eo = l; + if (s[l]) + pmatch[0].rm_eo++; + } + memcpy(s1, s, l); + /* make sure we remove *all* of the separator chars */ + do { + s1[l] = '\0'; + } while (++l < pmatch[0].rm_eo); + nextword(&s1); + s += pmatch[0].rm_eo; + } while (*s); + return n; + } + if (c[0] == '\0') { /* null split */ + while (*s) { + *s1++ = *s++; + *s1++ = '\0'; + n++; + } + return n; + } + if (c[0] != ' ') { /* single-character split */ + if (icase) { + c[0] = toupper(c[0]); + c[1] = tolower(c[1]); + } + if (*s1) + n++; + while ((s1 = strpbrk(s1, c)) != NULL) { + *s1++ = '\0'; + n++; + } + return n; + } + /* space split */ + while (*s) { + s = skip_whitespace(s); + if (!*s) + break; + n++; + while (*s && !isspace(*s)) + *s1++ = *s++; + *s1++ = '\0'; + } + return n; +} + +static void split_f0(void) +{ +/* static char *fstrings; */ +#define fstrings (G.split_f0__fstrings) + + int i, n; + char *s; + + if (is_f0_split) + return; + + is_f0_split = TRUE; + free(fstrings); + fsrealloc(0); + n = awk_split(getvar_s(intvar[F0]), &fsplitter.n, &fstrings); + fsrealloc(n); + s = fstrings; + for (i = 0; i < n; i++) { + Fields[i].string = nextword(&s); + Fields[i].type |= (VF_FSTR | VF_USER | VF_DIRTY); + } + + /* set NF manually to avoid side effects */ + clrvar(intvar[NF]); + intvar[NF]->type = VF_NUMBER | VF_SPECIAL; + intvar[NF]->number = nfields; +#undef fstrings +} + +/* perform additional actions when some internal variables changed */ +static void handle_special(var *v) +{ + int n; + char *b; + const char *sep, *s; + int sl, l, len, i, bsize; + + if (!(v->type & VF_SPECIAL)) + return; + + if (v == intvar[NF]) { + n = (int)getvar_i(v); + fsrealloc(n); + + /* recalculate $0 */ + sep = getvar_s(intvar[OFS]); + sl = strlen(sep); + b = NULL; + len = 0; + for (i = 0; i < n; i++) { + s = getvar_s(&Fields[i]); + l = strlen(s); + if (b) { + memcpy(b+len, sep, sl); + len += sl; + } + b = qrealloc(b, len+l+sl, &bsize); + memcpy(b+len, s, l); + len += l; + } + if (b) + b[len] = '\0'; + setvar_p(intvar[F0], b); + is_f0_split = TRUE; + + } else if (v == intvar[F0]) { + is_f0_split = FALSE; + + } else if (v == intvar[FS]) { + /* + * The POSIX-2008 standard says that changing FS should have no effect on the + * current input line, but only on the next one. The language is: + * + * > Before the first reference to a field in the record is evaluated, the record + * > shall be split into fields, according to the rules in Regular Expressions, + * > using the value of FS that was current at the time the record was read. + * + * So, split up current line before assignment to FS: + */ + 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); + /* right here v is invalid. Just to note... */ + } +} + +/* step through func/builtin/etc arguments */ +static node *nextarg(node **pn) +{ + node *n; + + n = *pn; + if (n && (n->info & OPCLSMASK) == OC_COMMA) { + *pn = n->r.n; + n = n->l.n; + } else { + *pn = NULL; + } + return n; +} + +static void hashwalk_init(var *v, xhash *array) +{ + hash_item *hi; + unsigned i; + walker_list *w; + walker_list *prev_walker; + + if (v->type & VF_WALK) { + prev_walker = v->x.walker; + } else { + v->type |= VF_WALK; + prev_walker = NULL; + } + debug_printf_walker("hashwalk_init: prev_walker:%p\n", prev_walker); + + w = v->x.walker = xzalloc(sizeof(*w) + array->glen + 1); /* why + 1? */ + debug_printf_walker(" walker@%p=%p\n", &v->x.walker, w); + w->cur = w->end = w->wbuf; + w->prev = prev_walker; + for (i = 0; i < array->csize; i++) { + hi = array->items[i]; + while (hi) { + strcpy(w->end, hi->name); + nextword(&w->end); + hi = hi->next; + } + } +} + +static int hashwalk_next(var *v) +{ + walker_list *w = v->x.walker; + + if (w->cur >= w->end) { + walker_list *prev_walker = w->prev; + + debug_printf_walker("end of iteration, free(walker@%p:%p), prev_walker:%p\n", &v->x.walker, w, prev_walker); + free(w); + v->x.walker = prev_walker; + return FALSE; + } + + setvar_s(v, nextword(&w->cur)); + return TRUE; +} + +/* evaluate node, return 1 when result is true, 0 otherwise */ +static int ptest(node *pattern) +{ + /* ptest__v is "static": to save stack space? */ + return istrue(evaluate(pattern, &G.ptest__v)); +} + +/* read next record from stream rsm into a variable v */ +static int awk_getline(rstream *rsm, var *v) +{ + char *b; + regmatch_t pmatch[2]; + int size, a, p, pp = 0; + int fd, so, eo, r, rp; + char c, *m, *s; + + debug_printf_eval("entered %s()\n", __func__); + + /* we're using our own buffer since we need access to accumulating + * characters + */ + fd = fileno(rsm->F); + m = rsm->buffer; + a = rsm->adv; + p = rsm->pos; + size = rsm->size; + c = (char) rsplitter.n.info; + rp = 0; + + if (!m) + m = qrealloc(m, 256, &size); + + do { + b = m + a; + so = eo = p; + r = 1; + if (p > 0) { + if ((rsplitter.n.info & OPCLSMASK) == OC_REGEXP) { + if (regexec(icase ? rsplitter.n.r.ire : rsplitter.n.l.re, + b, 1, pmatch, 0) == 0) { + so = pmatch[0].rm_so; + eo = pmatch[0].rm_eo; + if (b[eo] != '\0') + break; + } + } else if (c != '\0') { + s = strchr(b+pp, c); + if (!s) + s = memchr(b+pp, '\0', p - pp); + if (s) { + so = eo = s-b; + eo++; + break; + } + } else { + while (b[rp] == '\n') + rp++; + s = strstr(b+rp, "\n\n"); + if (s) { + so = eo = s-b; + while (b[eo] == '\n') + eo++; + if (b[eo] != '\0') + break; + } + } + } + + if (a > 0) { + memmove(m, m+a, p+1); + b = m; + a = 0; + } + + m = qrealloc(m, a+p+128, &size); + b = m + a; + pp = p; + p += safe_read(fd, b+p, size-p-1); + if (p < pp) { + p = 0; + r = 0; + setvar_i(intvar[ERRNO], errno); + } + b[p] = '\0'; + + } while (p > pp); + + if (p == 0) { + r--; + } else { + c = b[so]; b[so] = '\0'; + setvar_s(v, b+rp); + v->type |= VF_USER; + b[so] = c; + c = b[eo]; b[eo] = '\0'; + setvar_s(intvar[RT], b+so); + b[eo] = c; + } + + rsm->buffer = m; + rsm->adv = a + eo; + rsm->pos = p - eo; + rsm->size = size; + + debug_printf_eval("returning from %s(): %d\n", __func__, r); + + return r; +} + +static int fmt_num(char *b, int size, const char *format, double n, int int_as_int) +{ + int r = 0; + char c; + const char *s = format; + + if (int_as_int && n == (long long)n) { + r = snprintf(b, size, "%lld", (long long)n); + } else { + do { c = *s; } while (c && *++s); + if (strchr("diouxX", c)) { + r = snprintf(b, size, format, (int)n); + } else if (strchr("eEfgG", c)) { + r = snprintf(b, size, format, n); + } else { + syntax_error(EMSG_INV_FMT); + } + } + return r; +} + +/* formatted output into an allocated buffer, return ptr to buffer */ +static char *awk_printf(node *n) +{ + char *b = NULL; + char *fmt, *s, *f; + const char *s1; + int i, j, incr, bsize; + char c, c1; + var *v, *arg; + + v = nvalloc(1); + fmt = f = xstrdup(getvar_s(evaluate(nextarg(&n), v))); + + i = 0; + while (*f) { + s = f; + while (*f && (*f != '%' || *++f == '%')) + f++; + while (*f && !isalpha(*f)) { + if (*f == '*') + syntax_error("%*x formats are not supported"); + f++; + } + + incr = (f - s) + MAXVARFMT; + b = qrealloc(b, incr + i, &bsize); + c = *f; + if (c != '\0') + f++; + c1 = *f; + *f = '\0'; + arg = evaluate(nextarg(&n), v); + + j = i; + if (c == 'c' || !c) { + i += sprintf(b+i, s, is_numeric(arg) ? + (char)getvar_i(arg) : *getvar_s(arg)); + } else if (c == 's') { + s1 = getvar_s(arg); + b = qrealloc(b, incr+i+strlen(s1), &bsize); + i += sprintf(b+i, s, s1); + } else { + i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE); + } + *f = c1; + + /* if there was an error while sprintf, return value is negative */ + if (i < j) + i = j; + } + + free(fmt); + nvfree(v); + b = xrealloc(b, i + 1); + b[i] = '\0'; + return b; +} + +/* Common substitution routine. + * Replace (nm)'th substring of (src) that matches (rn) with (repl), + * store result into (dest), return number of substitutions. + * If nm = 0, replace all matches. + * If src or dst is NULL, use $0. + * If subexp != 0, enable subexpression matching (\1-\9). + */ +static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int subexp) +{ + char *resbuf; + const char *sp; + int match_no, residx, replen, resbufsize; + int regexec_flags; + regmatch_t pmatch[10]; + regex_t sreg, *regex; + + resbuf = NULL; + residx = 0; + match_no = 0; + regexec_flags = 0; + regex = as_regex(rn, &sreg); + sp = getvar_s(src ? src : intvar[F0]); + replen = strlen(repl); + while (regexec(regex, sp, 10, pmatch, regexec_flags) == 0) { + int so = pmatch[0].rm_so; + int eo = pmatch[0].rm_eo; + + //bb_error_msg("match %u: [%u,%u] '%s'%p", match_no+1, so, eo, sp,sp); + resbuf = qrealloc(resbuf, residx + eo + replen, &resbufsize); + memcpy(resbuf + residx, sp, eo); + residx += eo; + if (++match_no >= nm) { + const char *s; + int nbs; + + /* replace */ + residx -= (eo - so); + nbs = 0; + for (s = repl; *s; s++) { + char c = resbuf[residx++] = *s; + if (c == '\\') { + nbs++; + continue; + } + if (c == '&' || (subexp && c >= '0' && c <= '9')) { + int j; + residx -= ((nbs + 3) >> 1); + j = 0; + if (c != '&') { + j = c - '0'; + nbs++; + } + if (nbs % 2) { + resbuf[residx++] = c; + } else { + int n = pmatch[j].rm_eo - pmatch[j].rm_so; + resbuf = qrealloc(resbuf, residx + replen + n, &resbufsize); + memcpy(resbuf + residx, sp + pmatch[j].rm_so, n); + residx += n; + } + } + nbs = 0; + } + } + + regexec_flags = REG_NOTBOL; + sp += eo; + if (match_no == nm) + break; + if (eo == so) { + /* Empty match (e.g. "b*" will match anywhere). + * Advance by one char. */ +//BUG (bug 1333): +//gsub(/\info; + op = op->l.n; + + av[2] = av[3] = NULL; + for (i = 0; i < 4 && op; i++) { + an[i] = nextarg(&op); + if (isr & 0x09000000) + av[i] = evaluate(an[i], &tv[i]); + if (isr & 0x08000000) + as[i] = getvar_s(av[i]); + isr >>= 1; + } + + nargs = i; + if ((uint32_t)nargs < (info >> 30)) + syntax_error(EMSG_TOO_FEW_ARGS); + + info &= OPNMASK; + switch (info) { + + case B_a2: + if (ENABLE_FEATURE_AWK_LIBM) + setvar_i(res, atan2(getvar_i(av[0]), getvar_i(av[1]))); + else + syntax_error(EMSG_NO_MATH); + break; + + case B_sp: { + char *s, *s1; + + if (nargs > 2) { + spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ? + an[2] : mk_splitter(getvar_s(evaluate(an[2], &tv[2])), &tspl); + } else { + spl = &fsplitter.n; + } + + n = awk_split(as[0], spl, &s); + s1 = s; + clear_array(iamarray(av[1])); + for (i = 1; i <= n; i++) + setari_u(av[1], i, nextword(&s)); + free(s1); + setvar_i(res, n); + break; + } + + case B_ss: { + char *s; + + l = strlen(as[0]); + i = getvar_i(av[1]) - 1; + if (i > l) + i = l; + if (i < 0) + i = 0; + n = (nargs > 2) ? getvar_i(av[2]) : l-i; + if (n < 0) + n = 0; + s = xstrndup(as[0]+i, n); + setvar_p(res, s); + break; + } + + /* Bitwise ops must assume that operands are unsigned. GNU Awk 3.1.5: + * awk '{ print or(-1,1) }' gives "4.29497e+09", not "-2.xxxe+09" */ + case B_an: + setvar_i(res, getvar_i_int(av[0]) & getvar_i_int(av[1])); + break; + + case B_co: + setvar_i(res, ~getvar_i_int(av[0])); + break; + + case B_ls: + setvar_i(res, getvar_i_int(av[0]) << getvar_i_int(av[1])); + break; + + case B_or: + setvar_i(res, getvar_i_int(av[0]) | getvar_i_int(av[1])); + break; + + case B_rs: + setvar_i(res, getvar_i_int(av[0]) >> getvar_i_int(av[1])); + break; + + case B_xo: + setvar_i(res, getvar_i_int(av[0]) ^ getvar_i_int(av[1])); + break; + + case B_lo: + case B_up: { + char *s, *s1; + s1 = s = xstrdup(as[0]); + while (*s1) { + //*s1 = (info == B_up) ? toupper(*s1) : tolower(*s1); + if ((unsigned char)((*s1 | 0x20) - 'a') <= ('z' - 'a')) + *s1 = (info == B_up) ? (*s1 & 0xdf) : (*s1 | 0x20); + s1++; + } + setvar_p(res, s); + break; + } + + case B_ix: + n = 0; + ll = strlen(as[1]); + l = strlen(as[0]) - ll; + if (ll > 0 && l >= 0) { + if (!icase) { + char *s = strstr(as[0], as[1]); + if (s) + n = (s - as[0]) + 1; + } else { + /* this piece of code is terribly slow and + * really should be rewritten + */ + for (i = 0; i <= l; i++) { + if (strncasecmp(as[0]+i, as[1], ll) == 0) { + n = i+1; + break; + } + } + } + } + setvar_i(res, n); + break; + + case B_ti: + if (nargs > 1) + tt = getvar_i(av[1]); + else + time(&tt); + //s = (nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y"; + i = strftime(g_buf, MAXVARFMT, + ((nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y"), + localtime(&tt)); + g_buf[i] = '\0'; + setvar_s(res, g_buf); + break; + + case B_mt: + setvar_i(res, do_mktime(as[0])); + break; + + case B_ma: + re = as_regex(an[1], &sreg); + n = regexec(re, as[0], 1, pmatch, 0); + if (n == 0) { + pmatch[0].rm_so++; + pmatch[0].rm_eo++; + } else { + pmatch[0].rm_so = 0; + pmatch[0].rm_eo = -1; + } + setvar_i(newvar("RSTART"), pmatch[0].rm_so); + setvar_i(newvar("RLENGTH"), pmatch[0].rm_eo - pmatch[0].rm_so); + setvar_i(res, pmatch[0].rm_so); + if (re == &sreg) + regfree(re); + break; + + case B_ge: + awk_sub(an[0], as[1], getvar_i(av[2]), av[3], res, TRUE); + break; + + case B_gs: + setvar_i(res, awk_sub(an[0], as[1], 0, av[2], av[2], FALSE)); + break; + + case B_su: + setvar_i(res, awk_sub(an[0], as[1], 1, av[2], av[2], FALSE)); + break; + } + + nvfree(tv); + return res; +#undef tspl +} + +/* + * Evaluate node - the heart of the program. Supplied with subtree + * and place where to store result. returns ptr to result. + */ +#define XC(n) ((n) >> 8) + +static var *evaluate(node *op, var *res) +{ +/* This procedure is recursive so we should count every byte */ +#define fnargs (G.evaluate__fnargs) +/* seed is initialized to 1 */ +#define seed (G.evaluate__seed) +#define sreg (G.evaluate__sreg) + + var *v1; + + if (!op) + return setvar_s(res, NULL); + + debug_printf_eval("entered %s()\n", __func__); + + v1 = nvalloc(2); + + while (op) { + struct { + var *v; + const char *s; + } L = L; /* for compiler */ + struct { + var *v; + const char *s; + } R = R; + double L_d = L_d; + uint32_t opinfo; + int opn; + node *op1; + + opinfo = op->info; + opn = (opinfo & OPNMASK); + g_lineno = op->lineno; + op1 = op->l.n; + debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn); + + /* "delete" is special: + * "delete array[var--]" must evaluate index expr only once, + * must not evaluate it in "execute inevitable things" part. + */ + if (XC(opinfo & OPCLSMASK) == XC(OC_DELETE)) { + uint32_t info = op1->info & OPCLSMASK; + var *v; + + debug_printf_eval("DELETE\n"); + if (info == OC_VAR) { + v = op1->l.v; + } else if (info == OC_FNARG) { + v = &fnargs[op1->l.aidx]; + } else { + syntax_error(EMSG_NOT_ARRAY); + } + if (op1->r.n) { /* array ref? */ + const char *s; + s = getvar_s(evaluate(op1->r.n, v1)); + hash_remove(iamarray(v), s); + } else { + clear_array(iamarray(v)); + } + goto next; + } + + /* execute inevitable things */ + if (opinfo & OF_RES1) + L.v = evaluate(op1, v1); + if (opinfo & OF_RES2) + R.v = evaluate(op->r.n, v1+1); + if (opinfo & OF_STR1) { + L.s = getvar_s(L.v); + debug_printf_eval("L.s:'%s'\n", L.s); + } + if (opinfo & OF_STR2) { + R.s = getvar_s(R.v); + debug_printf_eval("R.s:'%s'\n", R.s); + } + if (opinfo & OF_NUM1) { + L_d = getvar_i(L.v); + debug_printf_eval("L_d:%f\n", L_d); + } + + debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK)); + switch (XC(opinfo & OPCLSMASK)) { + + /* -- iterative node type -- */ + + /* test pattern */ + case XC( OC_TEST ): + if ((op1->info & OPCLSMASK) == OC_COMMA) { + /* it's range pattern */ + if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) { + op->info |= OF_CHECKED; + if (ptest(op1->r.n)) + op->info &= ~OF_CHECKED; + op = op->a.n; + } else { + op = op->r.n; + } + } else { + op = ptest(op1) ? op->a.n : op->r.n; + } + break; + + /* just evaluate an expression, also used as unconditional jump */ + case XC( OC_EXEC ): + break; + + /* branch, used in if-else and various loops */ + case XC( OC_BR ): + op = istrue(L.v) ? op->a.n : op->r.n; + break; + + /* initialize for-in loop */ + case XC( OC_WALKINIT ): + hashwalk_init(L.v, iamarray(R.v)); + break; + + /* get next array item */ + case XC( OC_WALKNEXT ): + op = hashwalk_next(L.v) ? op->a.n : op->r.n; + break; + + case XC( OC_PRINT ): + case XC( OC_PRINTF ): { + FILE *F = stdout; + + if (op->r.n) { + rstream *rsm = newfile(R.s); + if (!rsm->F) { + if (opn == '|') { + rsm->F = popen(R.s, "w"); + if (rsm->F == NULL) + bb_perror_msg_and_die("popen"); + rsm->is_pipe = 1; + } else { + rsm->F = xfopen(R.s, opn=='w' ? "w" : "a"); + } + } + F = rsm->F; + } + + if ((opinfo & OPCLSMASK) == OC_PRINT) { + if (!op1) { + fputs(getvar_s(intvar[F0]), F); + } else { + while (op1) { + var *v = evaluate(nextarg(&op1), v1); + if (v->type & VF_NUMBER) { + fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[OFMT]), + getvar_i(v), TRUE); + fputs(g_buf, F); + } else { + fputs(getvar_s(v), F); + } + + if (op1) + fputs(getvar_s(intvar[OFS]), F); + } + } + fputs(getvar_s(intvar[ORS]), F); + + } else { /* OC_PRINTF */ + char *s = awk_printf(op1); + fputs(s, F); + free(s); + } + fflush(F); + break; + } + + /* case XC( OC_DELETE ): - moved to happen before arg evaluation */ + + case XC( OC_NEWSOURCE ): + g_progname = op->l.new_progname; + break; + + case XC( OC_RETURN ): + copyvar(res, L.v); + break; + + case XC( OC_NEXTFILE ): + nextfile = TRUE; + case XC( OC_NEXT ): + nextrec = TRUE; + case XC( OC_DONE ): + clrvar(res); + break; + + case XC( OC_EXIT ): + awk_exit(L_d); + + /* -- recursive node type -- */ + + case XC( OC_VAR ): + debug_printf_eval("VAR\n"); + L.v = op->l.v; + if (L.v == intvar[NF]) + split_f0(); + goto v_cont; + + case XC( OC_FNARG ): + debug_printf_eval("FNARG[%d]\n", op->l.aidx); + L.v = &fnargs[op->l.aidx]; + v_cont: + res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v; + break; + + case XC( OC_IN ): + setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0); + break; + + case XC( OC_REGEXP ): + op1 = op; + L.s = getvar_s(intvar[F0]); + goto re_cont; + + case XC( OC_MATCH ): + op1 = op->r.n; + re_cont: + { + regex_t *re = as_regex(op1, &sreg); + int i = regexec(re, L.s, 0, NULL, 0); + if (re == &sreg) + regfree(re); + setvar_i(res, (i == 0) ^ (opn == '!')); + } + break; + + case XC( OC_MOVE ): + debug_printf_eval("MOVE\n"); + /* if source is a temporary string, jusk relink it to dest */ +//Disabled: if R.v is numeric but happens to have cached R.v->string, +//then L.v ends up being a string, which is wrong +// if (R.v == v1+1 && R.v->string) { +// res = setvar_p(L.v, R.v->string); +// R.v->string = NULL; +// } else { + res = copyvar(L.v, R.v); +// } + break; + + case XC( OC_TERNARY ): + if ((op->r.n->info & OPCLSMASK) != OC_COLON) + syntax_error(EMSG_POSSIBLE_ERROR); + res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res); + break; + + case XC( OC_FUNC ): { + var *vbeg, *v; + const char *sv_progname; + + /* The body might be empty, still has to eval the args */ + if (!op->r.n->info && !op->r.f->body.first) + syntax_error(EMSG_UNDEF_FUNC); + + vbeg = v = nvalloc(op->r.f->nargs + 1); + while (op1) { + var *arg = evaluate(nextarg(&op1), v1); + copyvar(v, arg); + v->type |= VF_CHILD; + v->x.parent = arg; + if (++v - vbeg >= op->r.f->nargs) + break; + } + + v = fnargs; + fnargs = vbeg; + sv_progname = g_progname; + + res = evaluate(op->r.f->body.first, res); + + g_progname = sv_progname; + nvfree(fnargs); + fnargs = v; + + break; + } + + case XC( OC_GETLINE ): + case XC( OC_PGETLINE ): { + rstream *rsm; + int i; + + if (op1) { + rsm = newfile(L.s); + if (!rsm->F) { + if ((opinfo & OPCLSMASK) == OC_PGETLINE) { + rsm->F = popen(L.s, "r"); + rsm->is_pipe = TRUE; + } else { + rsm->F = fopen_for_read(L.s); /* not xfopen! */ + } + } + } else { + if (!iF) + iF = next_input_file(); + rsm = iF; + } + + if (!rsm || !rsm->F) { + setvar_i(intvar[ERRNO], errno); + setvar_i(res, -1); + break; + } + + if (!op->r.n) + R.v = intvar[F0]; + + i = awk_getline(rsm, R.v); + if (i > 0 && !op1) { + incvar(intvar[FNR]); + incvar(intvar[NR]); + } + setvar_i(res, i); + break; + } + + /* simple builtins */ + case XC( OC_FBLTIN ): { + double R_d = R_d; /* for compiler */ + + switch (opn) { + case F_in: + R_d = (long long)L_d; + break; + + case F_rn: + R_d = (double)rand() / (double)RAND_MAX; + break; + + case F_co: + if (ENABLE_FEATURE_AWK_LIBM) { + R_d = cos(L_d); + break; + } + + case F_ex: + if (ENABLE_FEATURE_AWK_LIBM) { + R_d = exp(L_d); + break; + } + + case F_lg: + if (ENABLE_FEATURE_AWK_LIBM) { + R_d = log(L_d); + break; + } + + case F_si: + if (ENABLE_FEATURE_AWK_LIBM) { + R_d = sin(L_d); + break; + } + + case F_sq: + if (ENABLE_FEATURE_AWK_LIBM) { + R_d = sqrt(L_d); + break; + } + + syntax_error(EMSG_NO_MATH); + break; + + case F_sr: + R_d = (double)seed; + seed = op1 ? (unsigned)L_d : (unsigned)time(NULL); + srand(seed); + break; + + case F_ti: + R_d = time(NULL); + break; + + case F_le: + debug_printf_eval("length: L.s:'%s'\n", L.s); + if (!op1) { + L.s = getvar_s(intvar[F0]); + debug_printf_eval("length: L.s='%s'\n", L.s); + } + else if (L.v->type & VF_ARRAY) { + R_d = L.v->x.array->nel; + debug_printf_eval("length: array_len:%d\n", L.v->x.array->nel); + break; + } + R_d = strlen(L.s); + break; + + case F_sy: + fflush_all(); + R_d = (ENABLE_FEATURE_ALLOW_EXEC && L.s && *L.s) + ? (system(L.s) >> 8) : 0; + break; + + case F_ff: + if (!op1) { + fflush(stdout); + } else if (L.s && *L.s) { + rstream *rsm = newfile(L.s); + fflush(rsm->F); + } else { + fflush_all(); + } + break; + + case F_cl: { + rstream *rsm; + int err = 0; + rsm = (rstream *)hash_search(fdhash, L.s); + debug_printf_eval("OC_FBLTIN F_cl rsm:%p\n", rsm); + if (rsm) { + debug_printf_eval("OC_FBLTIN F_cl " + "rsm->is_pipe:%d, ->F:%p\n", + rsm->is_pipe, rsm->F); + /* Can be NULL if open failed. Example: + * getline line <"doesnt_exist"; + * close("doesnt_exist"); <--- here rsm->F is NULL + */ + if (rsm->F) + err = rsm->is_pipe ? pclose(rsm->F) : fclose(rsm->F); + free(rsm->buffer); + hash_remove(fdhash, L.s); + } + if (err) + setvar_i(intvar[ERRNO], errno); + R_d = (double)err; + break; + } + } /* switch */ + setvar_i(res, R_d); + break; + } + + case XC( OC_BUILTIN ): + res = exec_builtin(op, res); + break; + + case XC( OC_SPRINTF ): + setvar_p(res, awk_printf(op1)); + break; + + case XC( OC_UNARY ): { + double Ld, R_d; + + Ld = R_d = getvar_i(R.v); + switch (opn) { + case 'P': + Ld = ++R_d; + goto r_op_change; + case 'p': + R_d++; + goto r_op_change; + case 'M': + Ld = --R_d; + goto r_op_change; + case 'm': + R_d--; + r_op_change: + setvar_i(R.v, R_d); + break; + case '!': + Ld = !istrue(R.v); + break; + case '-': + Ld = -R_d; + break; + } + setvar_i(res, Ld); + break; + } + + case XC( OC_FIELD ): { + int i = (int)getvar_i(R.v); + if (i < 0) + syntax_error(EMSG_NEGATIVE_FIELD); + if (i == 0) { + res = intvar[F0]; + } else { + split_f0(); + if (i > nfields) + fsrealloc(i); + res = &Fields[i - 1]; + } + break; + } + + /* concatenation (" ") and index joining (",") */ + case XC( OC_CONCAT ): + case XC( OC_COMMA ): { + const char *sep = ""; + if ((opinfo & OPCLSMASK) == OC_COMMA) + sep = getvar_s(intvar[SUBSEP]); + setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s)); + break; + } + + case XC( OC_LAND ): + setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0); + break; + + case XC( OC_LOR ): + setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n)); + break; + + case XC( OC_BINARY ): + case XC( OC_REPLACE ): { + double R_d = getvar_i(R.v); + debug_printf_eval("BINARY/REPLACE: R_d:%f opn:%c\n", R_d, opn); + switch (opn) { + case '+': + L_d += R_d; + break; + case '-': + L_d -= R_d; + break; + case '*': + L_d *= R_d; + break; + case '/': + if (R_d == 0) + syntax_error(EMSG_DIV_BY_ZERO); + L_d /= R_d; + break; + case '&': + if (ENABLE_FEATURE_AWK_LIBM) + L_d = pow(L_d, R_d); + else + syntax_error(EMSG_NO_MATH); + break; + case '%': + if (R_d == 0) + syntax_error(EMSG_DIV_BY_ZERO); + L_d -= (long long)(L_d / R_d) * R_d; + break; + } + debug_printf_eval("BINARY/REPLACE result:%f\n", L_d); + res = setvar_i(((opinfo & OPCLSMASK) == OC_BINARY) ? res : L.v, L_d); + break; + } + + case XC( OC_COMPARE ): { + int i = i; /* for compiler */ + double Ld; + + if (is_numeric(L.v) && is_numeric(R.v)) { + Ld = getvar_i(L.v) - getvar_i(R.v); + } else { + const char *l = getvar_s(L.v); + const char *r = getvar_s(R.v); + Ld = icase ? strcasecmp(l, r) : strcmp(l, r); + } + switch (opn & 0xfe) { + case 0: + i = (Ld > 0); + break; + case 2: + i = (Ld >= 0); + break; + case 4: + i = (Ld == 0); + break; + } + setvar_i(res, (i == 0) ^ (opn & 1)); + break; + } + + default: + syntax_error(EMSG_POSSIBLE_ERROR); + } /* switch */ + next: + if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS) + op = op->a.n; + if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS) + break; + if (nextrec) + break; + } /* while (op) */ + + nvfree(v1); + debug_printf_eval("returning from %s(): %p\n", __func__, res); + return res; +#undef fnargs +#undef seed +#undef sreg +} + + +/* -------- main & co. -------- */ + +static int awk_exit(int r) +{ + var tv; + unsigned i; + hash_item *hi; + + zero_out_var(&tv); + + if (!exiting) { + exiting = TRUE; + nextrec = FALSE; + evaluate(endseq.first, &tv); + } + + /* waiting for children */ + for (i = 0; i < fdhash->csize; i++) { + hi = fdhash->items[i]; + while (hi) { + if (hi->data.rs.F && hi->data.rs.is_pipe) + pclose(hi->data.rs.F); + hi = hi->next; + } + } + + exit(r); +} + +/* if expr looks like "var=value", perform assignment and return 1, + * otherwise return 0 */ +static int is_assignment(const char *expr) +{ + char *exprc, *val; + + if (!isalnum_(*expr) || (val = strchr(expr, '=')) == NULL) { + return FALSE; + } + + exprc = xstrdup(expr); + val = exprc + (val - expr); + *val++ = '\0'; + + unescape_string_in_place(val); + setvar_u(newvar(exprc), val); + free(exprc); + return TRUE; +} + +/* switch to next input file */ +static rstream *next_input_file(void) +{ +#define rsm (G.next_input_file__rsm) +#define files_happen (G.next_input_file__files_happen) + + FILE *F; + const char *fname, *ind; + + if (rsm.F) + fclose(rsm.F); + rsm.F = NULL; + rsm.pos = rsm.adv = 0; + + for (;;) { + if (getvar_i(intvar[ARGIND])+1 >= getvar_i(intvar[ARGC])) { + if (files_happen) + return NULL; + fname = "-"; + F = stdin; + break; + } + ind = getvar_s(incvar(intvar[ARGIND])); + fname = getvar_s(findvar(iamarray(intvar[ARGV]), ind)); + if (fname && *fname && !is_assignment(fname)) { + F = xfopen_stdin(fname); + break; + } + } + + files_happen = TRUE; + setvar_s(intvar[FILENAME], fname); + rsm.F = F; + return &rsm; +#undef rsm +#undef files_happen +} + +int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int awk_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opt; + char *opt_F; + llist_t *list_v = NULL; + llist_t *list_f = NULL; +#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS + llist_t *list_e = NULL; +#endif + int i, j; + var *v; + var tv; + char **envp; + char *vnames = (char *)vNames; /* cheat */ + char *vvalues = (char *)vValues; + + INIT_G(); + + /* Undo busybox.c, or else strtod may eat ','! This breaks parsing: + * $1,$2 == '$1,' '$2', NOT '$1' ',' '$2' */ + if (ENABLE_LOCALE_SUPPORT) + setlocale(LC_NUMERIC, "C"); + + zero_out_var(&tv); + + /* allocate global buffer */ + g_buf = xmalloc(MAXVARFMT + 1); + + vhash = hash_init(); + ahash = hash_init(); + fdhash = hash_init(); + fnhash = hash_init(); + + /* initialize variables */ + for (i = 0; *vnames; i++) { + intvar[i] = v = newvar(nextword(&vnames)); + if (*vvalues != '\377') + setvar_s(v, nextword(&vvalues)); + else + setvar_i(v, 0); + + if (*vnames == '*') { + v->type |= VF_SPECIAL; + vnames++; + } + } + + handle_special(intvar[FS]); + handle_special(intvar[RS]); + + newfile("/dev/stdin")->F = stdin; + newfile("/dev/stdout")->F = stdout; + newfile("/dev/stderr")->F = stderr; + + /* Huh, people report that sometimes environ is NULL. Oh well. */ + if (environ) for (envp = environ; *envp; envp++) { + /* environ is writable, thus we don't strdup it needlessly */ + char *s = *envp; + char *s1 = strchr(s, '='); + if (s1) { + *s1 = '\0'; + /* Both findvar and setvar_u take const char* + * as 2nd arg -> environment is not trashed */ + setvar_u(findvar(iamarray(intvar[ENVIRON]), s), s1 + 1); + *s1 = '='; + } + } + opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL); + argv += optind; + //argc -= optind; + if (opt & OPT_W) + bb_error_msg("warning: option -W is ignored"); + if (opt & OPT_F) { + unescape_string_in_place(opt_F); + setvar_s(intvar[FS], opt_F); + } + while (list_v) { + if (!is_assignment(llist_pop(&list_v))) + bb_show_usage(); + } + while (list_f) { + char *s = NULL; + FILE *from_file; + + g_progname = llist_pop(&list_f); + from_file = xfopen_stdin(g_progname); + /* one byte is reserved for some trick in next_token */ + for (i = j = 1; j > 0; i += j) { + s = xrealloc(s, i + 4096); + j = fread(s + i, 1, 4094, from_file); + } + s[i] = '\0'; + fclose(from_file); + parse_program(s + 1); + free(s); + } + g_progname = "cmd. line"; +#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS + while (list_e) { + parse_program(llist_pop(&list_e)); + } +#endif + if (!(opt & (OPT_f | OPT_e))) { + if (!*argv) + bb_show_usage(); + parse_program(*argv++); + } + + /* fill in ARGV array */ + setari_u(intvar[ARGV], 0, "awk"); + i = 0; + while (*argv) + setari_u(intvar[ARGV], ++i, *argv++); + setvar_i(intvar[ARGC], i + 1); + + evaluate(beginseq.first, &tv); + if (!mainseq.first && !endseq.first) + awk_exit(EXIT_SUCCESS); + + /* input file could already be opened in BEGIN block */ + if (!iF) + iF = next_input_file(); + + /* passing through input files */ + while (iF) { + nextfile = FALSE; + setvar_i(intvar[FNR], 0); + + while ((i = awk_getline(iF, intvar[F0])) > 0) { + nextrec = FALSE; + incvar(intvar[NR]); + incvar(intvar[FNR]); + evaluate(mainseq.first, &tv); + + if (nextfile) + break; + } + + if (i < 0) + syntax_error(strerror(errno)); + + iF = next_input_file(); + } + + awk_exit(EXIT_SUCCESS); + /*return 0;*/ +} diff --git a/busybox/editors/cmp.c b/busybox/editors/cmp.c new file mode 100644 index 00000000000000..2a410fdec1de57 --- /dev/null +++ b/busybox/editors/cmp.c @@ -0,0 +1,142 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cmp implementation for busybox + * + * Copyright (C) 2000,2001 by Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config CMP +//config: bool "cmp (5.4 kb)" +//config: default y +//config: help +//config: cmp is used to compare two files and returns the result +//config: to standard output. + +//applet:IF_CMP(APPLET(cmp, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_CMP) += cmp.o + +//usage:#define cmp_trivial_usage +//usage: "[-l] [-s] FILE1 [FILE2" IF_DESKTOP(" [SKIP1 [SKIP2]]") "]" +//usage:#define cmp_full_usage "\n\n" +//usage: "Compare FILE1 with FILE2 (or stdin)\n" +//usage: "\n -l Write the byte numbers (decimal) and values (octal)" +//usage: "\n for all differing bytes" +//usage: "\n -s Quiet" + +/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */ + +#include "libbb.h" + +static const char fmt_eof[] ALIGN1 = "cmp: EOF on %s\n"; +static const char fmt_differ[] ALIGN1 = "%s %s differ: char %"OFF_FMT"u, line %u\n"; +// This fmt_l_opt uses gnu-isms. SUSv3 would be "%.0s%.0s%"OFF_FMT"u %o %o\n" +static const char fmt_l_opt[] ALIGN1 = "%.0s%.0s%"OFF_FMT"u %3o %3o\n"; + +#define OPT_STR "sl" +#define CMP_OPT_s (1<<0) +#define CMP_OPT_l (1<<1) + +int cmp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cmp_main(int argc UNUSED_PARAM, char **argv) +{ + FILE *fp1, *fp2, *outfile = stdout; + const char *filename1, *filename2 = "-"; + off_t skip1 = 0, skip2 = 0, char_pos = 0; + int line_pos = 1; /* Hopefully won't overflow... */ + const char *fmt; + int c1, c2; + unsigned opt; + int retval = 0; + + opt = getopt32(argv, "^" + OPT_STR + "\0" "-1" + IF_DESKTOP(":?4") + IF_NOT_DESKTOP(":?2") + ":l--s:s--l" + ); + argv += optind; + + filename1 = *argv; + if (*++argv) { + filename2 = *argv; + if (ENABLE_DESKTOP && *++argv) { + skip1 = XATOOFF(*argv); + if (*++argv) { + skip2 = XATOOFF(*argv); + } + } + } + + xfunc_error_retval = 2; /* missing file results in exitcode 2 */ + if (opt & CMP_OPT_s) + logmode = 0; /* -s suppresses open error messages */ + fp1 = xfopen_stdin(filename1); + fp2 = xfopen_stdin(filename2); + if (fp1 == fp2) { /* Paranoia check... stdin == stdin? */ + /* Note that we don't bother reading stdin. Neither does gnu wc. + * But perhaps we should, so that other apps down the chain don't + * get the input. Consider 'echo hello | (cmp - - && cat -)'. + */ + return 0; + } + logmode = LOGMODE_STDIO; + + if (opt & CMP_OPT_l) + fmt = fmt_l_opt; + else + fmt = fmt_differ; + + if (ENABLE_DESKTOP) { + while (skip1) { getc(fp1); skip1--; } + while (skip2) { getc(fp2); skip2--; } + } + do { + c1 = getc(fp1); + c2 = getc(fp2); + ++char_pos; + if (c1 != c2) { /* Remember: a read error may have occurred. */ + retval = 1; /* But assume the files are different for now. */ + if (c2 == EOF) { + /* We know that fp1 isn't at EOF or in an error state. But to + * save space below, things are setup to expect an EOF in fp1 + * if an EOF occurred. So, swap things around. + */ + fp1 = fp2; + filename1 = filename2; + c1 = c2; + } + if (c1 == EOF) { + die_if_ferror(fp1, filename1); + fmt = fmt_eof; /* Well, no error, so it must really be EOF. */ + outfile = stderr; + /* There may have been output to stdout (option -l), so + * make sure we fflush before writing to stderr. */ + fflush_all(); + } + if (!(opt & CMP_OPT_s)) { + if (opt & CMP_OPT_l) { + line_pos = c1; /* line_pos is unused in the -l case. */ + } + fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2); + if (opt) { /* This must be -l since not -s. */ + /* If we encountered an EOF, + * the while check will catch it. */ + continue; + } + } + break; + } + if (c1 == '\n') { + ++line_pos; + } + } while (c1 != EOF); + + die_if_ferror(fp1, filename1); + die_if_ferror(fp2, filename2); + + fflush_stdout_and_exit(retval); +} diff --git a/busybox/editors/diff.c b/busybox/editors/diff.c new file mode 100644 index 00000000000000..1462a9b1865637 --- /dev/null +++ b/busybox/editors/diff.c @@ -0,0 +1,1058 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini diff implementation for busybox, adapted from OpenBSD diff. + * + * Copyright (C) 2010 by Matheus Izvekov + * Copyright (C) 2006 by Robert Sullivan + * Copyright (c) 2003 Todd C. Miller + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +/* + * The following code uses an algorithm due to Harold Stone, + * which finds a pair of longest identical subsequences in + * the two files. + * + * The major goal is to generate the match vector J. + * J[i] is the index of the line in file1 corresponding + * to line i in file0. J[i] = 0 if there is no + * such line in file1. + * + * Lines are hashed so as to work in core. All potential + * matches are located by sorting the lines of each file + * on the hash (called "value"). In particular, this + * collects the equivalence classes in file1 together. + * Subroutine equiv replaces the value of each line in + * file0 by the index of the first element of its + * matching equivalence in (the reordered) file1. + * To save space equiv squeezes file1 into a single + * array member in which the equivalence classes + * are simply concatenated, except that their first + * members are flagged by changing sign. + * + * Next the indices that point into member are unsorted into + * array class according to the original order of file0. + * + * The cleverness lies in routine stone. This marches + * through the lines of file0, developing a vector klist + * of "k-candidates". At step i a k-candidate is a matched + * pair of lines x,y (x in file0, y in file1) such that + * there is a common subsequence of length k + * between the first i lines of file0 and the first y + * lines of file1, but there is no such subsequence for + * any smaller y. x is the earliest possible mate to y + * that occurs in such a subsequence. + * + * Whenever any of the members of the equivalence class of + * lines in file1 matable to a line in file0 has serial number + * less than the y of some k-candidate, that k-candidate + * with the smallest such y is replaced. The new + * k-candidate is chained (via pred) to the current + * k-1 candidate so that the actual subsequence can + * be recovered. When a member has serial number greater + * that the y of all k-candidates, the klist is extended. + * At the end, the longest subsequence is pulled out + * and placed in the array J by unravel + * + * With J in hand, the matches there recorded are + * checked against reality to assure that no spurious + * matches have crept in due to hashing. If they have, + * they are broken, and "jackpot" is recorded--a harmless + * matter except that a true match for a spuriously + * mated line may now be unnecessarily reported as a change. + * + * Much of the complexity of the program comes simply + * from trying to minimize core utilization and + * maximize the range of doable problems by dynamically + * allocating what is needed and reusing what is not. + * The core requirements for problems larger than somewhat + * are (in words) 2*length(file0) + length(file1) + + * 3*(number of k-candidates installed), typically about + * 6n words for files of length n. + */ +//config:config DIFF +//config: bool "diff (13 kb)" +//config: default y +//config: help +//config: diff compares two files or directories and outputs the +//config: differences between them in a form that can be given to +//config: the patch command. +//config: +//config:config FEATURE_DIFF_LONG_OPTIONS +//config: bool "Enable long options" +//config: default y +//config: depends on DIFF && LONG_OPTS +//config: +//config:config FEATURE_DIFF_DIR +//config: bool "Enable directory support" +//config: default y +//config: depends on DIFF +//config: help +//config: This option enables support for directory and subdirectory +//config: comparison. + +//applet:IF_DIFF(APPLET(diff, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_DIFF) += diff.o + +//usage:#define diff_trivial_usage +//usage: "[-abBdiNqrTstw] [-L LABEL] [-S FILE] [-U LINES] FILE1 FILE2" +//usage:#define diff_full_usage "\n\n" +//usage: "Compare files line by line and output the differences between them.\n" +//usage: "This implementation supports unified diffs only.\n" +//usage: "\n -a Treat all files as text" +//usage: "\n -b Ignore changes in the amount of whitespace" +//usage: "\n -B Ignore changes whose lines are all blank" +//usage: "\n -d Try hard to find a smaller set of changes" +//usage: "\n -i Ignore case differences" +//usage: "\n -L Use LABEL instead of the filename in the unified header" +//usage: "\n -N Treat absent files as empty" +//usage: "\n -q Output only whether files differ" +//usage: "\n -r Recurse" +//usage: "\n -S Start with FILE when comparing directories" +//usage: "\n -T Make tabs line up by prefixing a tab when necessary" +//usage: "\n -s Report when two files are the same" +//usage: "\n -t Expand tabs to spaces in output" +//usage: "\n -U Output LINES lines of context" +//usage: "\n -w Ignore all whitespace" + +#include "libbb.h" +#include "common_bufsiz.h" + +#if 0 +# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__) +#else +# define dbg_error_msg(...) ((void)0) +#endif + +enum { /* print_status() and diffreg() return values */ + STATUS_SAME, /* files are the same */ + STATUS_DIFFER, /* files differ */ + STATUS_BINARY, /* binary files differ */ +}; + +enum { /* Commandline flags */ + FLAG_a, + FLAG_b, + FLAG_d, + FLAG_i, + FLAG_L, /* never used, handled by getopt32 */ + FLAG_N, + FLAG_q, + FLAG_r, + FLAG_s, + FLAG_S, /* never used, handled by getopt32 */ + FLAG_t, + FLAG_T, + FLAG_U, /* never used, handled by getopt32 */ + FLAG_w, + FLAG_u, /* ignored, this is the default */ + FLAG_p, /* not implemented */ + FLAG_B, + FLAG_E, /* not implemented */ +}; +#define FLAG(x) (1 << FLAG_##x) + +/* We cache file position to avoid excessive seeking */ +typedef struct FILE_and_pos_t { + FILE *ft_fp; + off_t ft_pos; +} FILE_and_pos_t; + +struct globals { + smallint exit_status; + int opt_U_context; + const char *other_dir; + char *label[2]; + struct stat stb[2]; +}; +#define G (*ptr_to_globals) +#define exit_status (G.exit_status ) +#define opt_U_context (G.opt_U_context ) +#define label (G.label ) +#define stb (G.stb ) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ + opt_U_context = 3; \ +} while (0) + +typedef int token_t; + +enum { + /* Public */ + TOK_EMPTY = 1 << 9, /* Line fully processed, you can proceed to the next */ + TOK_EOF = 1 << 10, /* File ended */ + /* Private (Only to be used by read_token() */ + TOK_EOL = 1 << 11, /* we saw EOL (sticky) */ + TOK_SPACE = 1 << 12, /* used -b code, means we are skipping spaces */ + SHIFT_EOF = (sizeof(token_t)*8 - 8) - 1, + CHAR_MASK = 0x1ff, /* 8th bit is used to distinguish EOF from 0xff */ +}; + +/* Restores full EOF from one 8th bit: */ +//#define TOK2CHAR(t) (((t) << SHIFT_EOF) >> SHIFT_EOF) +/* We don't really need the above, we only need to have EOF != any_real_char: */ +#define TOK2CHAR(t) ((t) & CHAR_MASK) + +static void seek_ft(FILE_and_pos_t *ft, off_t pos) +{ + if (ft->ft_pos != pos) { + ft->ft_pos = pos; + fseeko(ft->ft_fp, pos, SEEK_SET); + } +} + +/* Reads tokens from given fp, handling -b and -w flags + * The user must reset tok every line start + */ +static int read_token(FILE_and_pos_t *ft, token_t tok) +{ + tok |= TOK_EMPTY; + while (!(tok & TOK_EOL)) { + bool is_space; + int t; + + t = fgetc(ft->ft_fp); + if (t != EOF) + ft->ft_pos++; + is_space = (t == EOF || isspace(t)); + + /* If t == EOF (-1), set both TOK_EOF and TOK_EOL */ + tok |= (t & (TOK_EOF + TOK_EOL)); + /* Only EOL? */ + if (t == '\n') + tok |= TOK_EOL; + + if (option_mask32 & FLAG(i)) /* Handcoded tolower() */ + t = (t >= 'A' && t <= 'Z') ? t - ('A' - 'a') : t; + + if ((option_mask32 & FLAG(w)) && is_space) + continue; + + /* Trim char value to low 9 bits */ + t &= CHAR_MASK; + + if (option_mask32 & FLAG(b)) { + /* Was prev char whitespace? */ + if (tok & TOK_SPACE) { /* yes */ + if (is_space) /* this one too, ignore it */ + continue; + tok &= ~TOK_SPACE; + } else if (is_space) { + /* 1st whitespace char. + * Set TOK_SPACE and replace char by ' ' */ + t = TOK_SPACE + ' '; + } + } + /* Clear EMPTY */ + tok &= ~(TOK_EMPTY + CHAR_MASK); + /* Assign char value (low 9 bits) and maybe set TOK_SPACE */ + tok |= t; + break; + } +#if 0 + bb_error_msg("fp:%p tok:%x '%c'%s%s%s%s", fp, tok, tok & 0xff + , tok & TOK_EOF ? " EOF" : "" + , tok & TOK_EOL ? " EOL" : "" + , tok & TOK_EMPTY ? " EMPTY" : "" + , tok & TOK_SPACE ? " SPACE" : "" + ); +#endif + return tok; +} + +struct cand { + int x; + int y; + int pred; +}; + +static int search(const int *c, int k, int y, const struct cand *list) +{ + int i, j; + + if (list[c[k]].y < y) /* quick look for typical case */ + return k + 1; + + for (i = 0, j = k + 1;;) { + const int l = (i + j) >> 1; + if (l > i) { + const int t = list[c[l]].y; + if (t > y) + j = l; + else if (t < y) + i = l; + else + return l; + } else + return l + 1; + } +} + +static void stone(const int *a, int n, const int *b, int *J, int pref) +{ + const unsigned isq = isqrt(n); + const unsigned bound = + (option_mask32 & FLAG(d)) ? UINT_MAX : MAX(256, isq); + int clen = 1; + int clistlen = 100; + int k = 0; + struct cand *clist = xzalloc(clistlen * sizeof(clist[0])); + struct cand cand; + struct cand *q; + int *klist = xzalloc((n + 2) * sizeof(klist[0])); + /*clist[0] = (struct cand){0}; - xzalloc did it */ + /*klist[0] = 0; */ + + for (cand.x = 1; cand.x <= n; cand.x++) { + int j = a[cand.x], oldl = 0; + unsigned numtries = 0; + if (j == 0) + continue; + cand.y = -b[j]; + cand.pred = klist[0]; + do { + int l, tc; + if (cand.y <= clist[cand.pred].y) + continue; + l = search(klist, k, cand.y, clist); + if (l != oldl + 1) + cand.pred = klist[l - 1]; + if (l <= k && clist[klist[l]].y <= cand.y) + continue; + if (clen == clistlen) { + clistlen = clistlen * 11 / 10; + clist = xrealloc(clist, clistlen * sizeof(clist[0])); + } + clist[clen] = cand; + tc = klist[l]; + klist[l] = clen++; + if (l <= k) { + cand.pred = tc; + oldl = l; + numtries++; + } else { + k++; + break; + } + } while ((cand.y = b[++j]) > 0 && numtries < bound); + } + /* Unravel */ + for (q = clist + klist[k]; q->y; q = clist + q->pred) + J[q->x + pref] = q->y + pref; + free(klist); + free(clist); +} + +struct line { + /* 'serial' is not used in the beginning, so we reuse it + * to store line offsets, thus reducing memory pressure + */ + union { + unsigned serial; + off_t offset; + }; + unsigned value; +}; + +static void equiv(struct line *a, int n, struct line *b, int m, int *c) +{ + int i = 1, j = 1; + + while (i <= n && j <= m) { + if (a[i].value < b[j].value) + a[i++].value = 0; + else if (a[i].value == b[j].value) + a[i++].value = j; + else + j++; + } + while (i <= n) + a[i++].value = 0; + b[m + 1].value = 0; + j = 0; + while (++j <= m) { + c[j] = -b[j].serial; + while (b[j + 1].value == b[j].value) { + j++; + c[j] = b[j].serial; + } + } + c[j] = -1; +} + +static void unsort(const struct line *f, int l, int *b) +{ + int i; + int *a = xmalloc((l + 1) * sizeof(a[0])); + for (i = 1; i <= l; i++) + a[f[i].serial] = f[i].value; + for (i = 1; i <= l; i++) + b[i] = a[i]; + free(a); +} + +static int line_compar(const void *a, const void *b) +{ +#define l0 ((const struct line*)a) +#define l1 ((const struct line*)b) + int r = l0->value - l1->value; + if (r) + return r; + return l0->serial - l1->serial; +#undef l0 +#undef l1 +} + +static void fetch(FILE_and_pos_t *ft, const off_t *ix, int a, int b, int ch) +{ + int i, j, col; + for (i = a; i <= b; i++) { + seek_ft(ft, ix[i - 1]); + putchar(ch); + if (option_mask32 & FLAG(T)) + putchar('\t'); + for (j = 0, col = 0; j < ix[i] - ix[i - 1]; j++) { + int c = fgetc(ft->ft_fp); + if (c == EOF) { + puts("\n\\ No newline at end of file"); + return; + } + ft->ft_pos++; + if (c == '\t' && (option_mask32 & FLAG(t))) + do putchar(' '); while (++col & 7); + else { + putchar(c); + col++; + } + } + } +} + +/* Creates the match vector J, where J[i] is the index + * of the line in the new file corresponding to the line i + * in the old file. Lines start at 1 instead of 0, that value + * being used instead to denote no corresponding line. + * This vector is dynamically allocated and must be freed by the caller. + * + * * fp is an input parameter, where fp[0] and fp[1] are the open + * old file and new file respectively. + * * nlen is an output variable, where nlen[0] and nlen[1] + * gets the number of lines in the old and new file respectively. + * * ix is an output variable, where ix[0] and ix[1] gets + * assigned dynamically allocated vectors of the offsets of the lines + * of the old and new file respectively. These must be freed by the caller. + */ +static NOINLINE int *create_J(FILE_and_pos_t ft[2], int nlen[2], off_t *ix[2]) +{ + int *J, slen[2], *class, *member; + struct line *nfile[2], *sfile[2]; + int pref = 0, suff = 0, i, j, delta; + + /* Lines of both files are hashed, and in the process + * their offsets are stored in the array ix[fileno] + * where fileno == 0 points to the old file, and + * fileno == 1 points to the new one. + */ + for (i = 0; i < 2; i++) { + unsigned hash; + token_t tok; + size_t sz = 100; + nfile[i] = xmalloc((sz + 3) * sizeof(nfile[i][0])); + /* ft gets here without the correct position, cant use seek_ft */ + ft[i].ft_pos = 0; + fseeko(ft[i].ft_fp, 0, SEEK_SET); + + nlen[i] = 0; + /* We could zalloc nfile, but then zalloc starts showing in gprof at ~1% */ + nfile[i][0].offset = 0; + goto start; /* saves code */ + while (1) { + tok = read_token(&ft[i], tok); + if (!(tok & TOK_EMPTY)) { + /* Hash algorithm taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. */ + /*hash = hash * 128 - hash + TOK2CHAR(tok); + * gcc insists on optimizing above to "hash * 127 + ...", thus... */ + unsigned o = hash - TOK2CHAR(tok); + hash = hash * 128 - o; /* we want SPEED here */ + continue; + } + if (nlen[i]++ == sz) { + sz = sz * 3 / 2; + nfile[i] = xrealloc(nfile[i], (sz + 3) * sizeof(nfile[i][0])); + } + /* line_compar needs hashes fit into positive int */ + nfile[i][nlen[i]].value = hash & INT_MAX; + /* like ftello(ft[i].ft_fp) but faster (avoids lseek syscall) */ + nfile[i][nlen[i]].offset = ft[i].ft_pos; + if (tok & TOK_EOF) { + /* EOF counts as a token, so we have to adjust it here */ + nfile[i][nlen[i]].offset++; + break; + } +start: + hash = tok = 0; + } + /* Exclude lone EOF line from the end of the file, to make fetch()'s job easier */ + if (nfile[i][nlen[i]].offset - nfile[i][nlen[i] - 1].offset == 1) + nlen[i]--; + /* Now we copy the line offsets into ix */ + ix[i] = xmalloc((nlen[i] + 2) * sizeof(ix[i][0])); + for (j = 0; j < nlen[i] + 1; j++) + ix[i][j] = nfile[i][j].offset; + } + + /* length of prefix and suffix is calculated */ + for (; pref < nlen[0] && pref < nlen[1] && + nfile[0][pref + 1].value == nfile[1][pref + 1].value; + pref++); + for (; suff < nlen[0] - pref && suff < nlen[1] - pref && + nfile[0][nlen[0] - suff].value == nfile[1][nlen[1] - suff].value; + suff++); + /* Arrays are pruned by the suffix and prefix length, + * the result being sorted and stored in sfile[fileno], + * and their sizes are stored in slen[fileno] + */ + for (j = 0; j < 2; j++) { + sfile[j] = nfile[j] + pref; + slen[j] = nlen[j] - pref - suff; + for (i = 0; i <= slen[j]; i++) + sfile[j][i].serial = i; + qsort(sfile[j] + 1, slen[j], sizeof(*sfile[j]), line_compar); + } + /* nfile arrays are reused to reduce memory pressure + * The #if zeroed out section performs the same task as the + * one in the #else section. + * Peak memory usage is higher, but one array copy is avoided + * by not using unsort() + */ +#if 0 + member = xmalloc((slen[1] + 2) * sizeof(member[0])); + equiv(sfile[0], slen[0], sfile[1], slen[1], member); + free(nfile[1]); + + class = xmalloc((slen[0] + 1) * sizeof(class[0])); + for (i = 1; i <= slen[0]; i++) /* Unsorting */ + class[sfile[0][i].serial] = sfile[0][i].value; + free(nfile[0]); +#else + member = (int *)nfile[1]; + equiv(sfile[0], slen[0], sfile[1], slen[1], member); + member = xrealloc(member, (slen[1] + 2) * sizeof(member[0])); + + class = (int *)nfile[0]; + unsort(sfile[0], slen[0], (int *)nfile[0]); + class = xrealloc(class, (slen[0] + 2) * sizeof(class[0])); +#endif + J = xmalloc((nlen[0] + 2) * sizeof(J[0])); + /* The elements of J which fall inside the prefix and suffix regions + * are marked as unchanged, while the ones which fall outside + * are initialized with 0 (no matches), so that function stone can + * then assign them their right values + */ + for (i = 0, delta = nlen[1] - nlen[0]; i <= nlen[0]; i++) + J[i] = i <= pref ? i : + i > (nlen[0] - suff) ? (i + delta) : 0; + /* Here the magic is performed */ + stone(class, slen[0], member, J, pref); + J[nlen[0] + 1] = nlen[1] + 1; + + free(class); + free(member); + + /* Both files are rescanned, in an effort to find any lines + * which, due to limitations intrinsic to any hashing algorithm, + * are different but ended up confounded as the same + */ + for (i = 1; i <= nlen[0]; i++) { + if (!J[i]) + continue; + + seek_ft(&ft[0], ix[0][i - 1]); + seek_ft(&ft[1], ix[1][J[i] - 1]); + + for (j = J[i]; i <= nlen[0] && J[i] == j; i++, j++) { + token_t tok0 = 0, tok1 = 0; + do { + tok0 = read_token(&ft[0], tok0); + tok1 = read_token(&ft[1], tok1); + + if (((tok0 ^ tok1) & TOK_EMPTY) != 0 /* one is empty (not both) */ + || (!(tok0 & TOK_EMPTY) && TOK2CHAR(tok0) != TOK2CHAR(tok1)) + ) { + J[i] = 0; /* Break the correspondence */ + } + } while (!(tok0 & tok1 & TOK_EMPTY)); + } + } + + return J; +} + +static bool diff(FILE* fp[2], char *file[2]) +{ + int nlen[2]; + off_t *ix[2]; + FILE_and_pos_t ft[2]; + typedef struct { int a, b; } vec_t[2]; + vec_t *vec = NULL; + int i = 1, j, k, idx = -1; + bool anychange = false; + int *J; + + ft[0].ft_fp = fp[0]; + ft[1].ft_fp = fp[1]; + /* note that ft[i].ft_pos is unintitalized, create_J() + * must not assume otherwise */ + J = create_J(ft, nlen, ix); + + do { + bool nonempty = false; + + while (1) { + vec_t v; + + for (v[0].a = i; v[0].a <= nlen[0] && J[v[0].a] == J[v[0].a - 1] + 1; v[0].a++) + continue; + v[1].a = J[v[0].a - 1] + 1; + + for (v[0].b = v[0].a - 1; v[0].b < nlen[0] && !J[v[0].b + 1]; v[0].b++) + continue; + v[1].b = J[v[0].b + 1] - 1; + /* + * Indicate that there is a difference between lines a and b of the 'from' file + * to get to lines c to d of the 'to' file. If a is greater than b then there + * are no lines in the 'from' file involved and this means that there were + * lines appended (beginning at b). If c is greater than d then there are + * lines missing from the 'to' file. + */ + if (v[0].a <= v[0].b || v[1].a <= v[1].b) { + /* + * If this change is more than 'context' lines from the + * previous change, dump the record and reset it. + */ + int ct = (2 * opt_U_context) + 1; + if (idx >= 0 + && v[0].a > vec[idx][0].b + ct + && v[1].a > vec[idx][1].b + ct + ) { + break; + } + + for (j = 0; j < 2; j++) + 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)); + } + + i = v[0].b + 1; + if (i > nlen[0]) + break; + J[v[0].b] = v[1].b; + } + if (idx < 0 || ((option_mask32 & FLAG(B)) && !nonempty)) + goto cont; + if (!(option_mask32 & FLAG(q))) { + int lowa; + vec_t span, *cvp = vec; + + if (!anychange) { + /* Print the context/unidiff header first time through */ + printf("--- %s\n", label[0] ? label[0] : file[0]); + printf("+++ %s\n", label[1] ? label[1] : file[1]); + } + + printf("@@"); + for (j = 0; j < 2; j++) { + int a = span[j].a = MAX(1, (*cvp)[j].a - opt_U_context); + int b = span[j].b = MIN(nlen[j], vec[idx][j].b + opt_U_context); + + printf(" %c%d", j ? '+' : '-', MIN(a, b)); + if (a == b) + continue; + printf(",%d", (a < b) ? b - a + 1 : 0); + } + puts(" @@"); + /* + * Output changes in "unified" diff format--the old and new lines + * are printed together. + */ + for (lowa = span[0].a; ; lowa = (*cvp++)[0].b + 1) { + bool end = cvp > &vec[idx]; + fetch(&ft[0], ix[0], lowa, end ? span[0].b : (*cvp)[0].a - 1, ' '); + if (end) + break; + for (j = 0; j < 2; j++) + fetch(&ft[j], ix[j], (*cvp)[j].a, (*cvp)[j].b, j ? '+' : '-'); + } + } + anychange = true; + cont: + idx = -1; + } while (i <= nlen[0]); + + free(vec); + free(ix[0]); + free(ix[1]); + free(J); + return anychange; +} + +static int diffreg(char *file[2]) +{ + FILE *fp[2]; + bool binary = false, differ = false; + int status = STATUS_SAME, i; + + fp[0] = stdin; + fp[1] = stdin; + for (i = 0; i < 2; i++) { + int fd = STDIN_FILENO; + if (!LONE_DASH(file[i])) { + if (!(option_mask32 & FLAG(N))) { + fd = open_or_warn(file[i], O_RDONLY); + if (fd == -1) + goto out; + } else { + /* -N: if some file does not exist compare it like empty */ + fd = open(file[i], O_RDONLY); + if (fd == -1) + fd = xopen("/dev/null", O_RDONLY); + } + } + /* Our diff implementation is using seek. + * When we meet non-seekable file, we must make a temp copy. + */ + if (lseek(fd, 0, SEEK_SET) == -1 && errno == ESPIPE) { + char name[] = "/tmp/difXXXXXX"; + int fd_tmp = xmkstemp(name); + + unlink(name); + if (bb_copyfd_eof(fd, fd_tmp) < 0) + xfunc_die(); + 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; + char *const buf1 = buf0 + sz; + int j, k; + i = fread(buf0, 1, sz, fp[0]); + j = fread(buf1, 1, sz, fp[1]); + if (i != j) { + differ = true; + i = MIN(i, j); + } + if (i == 0) + break; + for (k = 0; k < i; k++) { + if (!buf0[k] || !buf1[k]) + binary = true; + if (buf0[k] != buf1[k]) + differ = true; + } + } + if (differ) { + if (binary && !(option_mask32 & FLAG(a))) + status = STATUS_BINARY; + else if (diff(fp, file)) + status = STATUS_DIFFER; + } + if (status != STATUS_SAME) + exit_status |= 1; +out: + fclose_if_not_stdin(fp[0]); + fclose_if_not_stdin(fp[1]); + + return status; +} + +static void print_status(int status, char *path[2]) +{ + switch (status) { + case STATUS_BINARY: + case STATUS_DIFFER: + if ((option_mask32 & FLAG(q)) || status == STATUS_BINARY) + printf("Files %s and %s differ\n", path[0], path[1]); + break; + case STATUS_SAME: + if (option_mask32 & FLAG(s)) + printf("Files %s and %s are identical\n", path[0], path[1]); + break; + } +} + +#if ENABLE_FEATURE_DIFF_DIR +struct dlist { + size_t len; + int s, e; + char **dl; +}; + +/* This function adds a filename to dl, the directory listing. */ +static int FAST_FUNC add_to_dirlist(const char *filename, + struct stat *sb UNUSED_PARAM, + void *userdata, int depth UNUSED_PARAM) +{ + struct dlist *const l = userdata; + const char *file = filename + l->len; + while (*file == '/') + file++; + l->dl = xrealloc_vector(l->dl, 6, l->e); + l->dl[l->e] = xstrdup(file); + l->e++; + return TRUE; +} + +/* If recursion is not set, this function adds the directory + * to the list and prevents recursive_action from recursing into it. + */ +static int FAST_FUNC skip_dir(const char *filename, + struct stat *sb, void *userdata, + int depth) +{ + if (!(option_mask32 & FLAG(r)) && depth) { + add_to_dirlist(filename, sb, userdata, depth); + return SKIP; + } + if (!(option_mask32 & FLAG(N))) { + /* -r without -N: no need to recurse into dirs + * which do not exist on the "other side". + * Testcase: diff -r /tmp / + * (it would recurse deep into /proc without this code) */ + struct dlist *const l = userdata; + filename += l->len; + if (filename[0]) { + struct stat osb; + char *othername = concat_path_file(G.other_dir, filename); + int r = stat(othername, &osb); + free(othername); + if (r != 0 || !S_ISDIR(osb.st_mode)) { + /* other dir doesn't have similarly named + * directory, don't recurse; return 1 upon + * exit, just like diffutils' diff */ + exit_status |= 1; + return SKIP; + } + } + } + return TRUE; +} + +static void diffdir(char *p[2], const char *s_start) +{ + struct dlist list[2]; + int i; + + memset(&list, 0, sizeof(list)); + for (i = 0; i < 2; i++) { + /*list[i].s = list[i].e = 0; - memset did it */ + /*list[i].dl = NULL; */ + + G.other_dir = p[1 - i]; + /* We need to trim root directory prefix. + * Using list.len to specify its length, + * add_to_dirlist will remove it. */ + list[i].len = strlen(p[i]); + recursive_action(p[i], ACTION_RECURSE | ACTION_FOLLOWLINKS, + add_to_dirlist, skip_dir, &list[i], 0); + /* Sort dl alphabetically. + * GNU diff does this ignoring any number of trailing dots. + * We don't, so for us dotted files almost always are + * first on the list. + */ + qsort_string_vector(list[i].dl, list[i].e); + /* If -S was set, find the starting point. */ + if (!s_start) + continue; + while (list[i].s < list[i].e && strcmp(list[i].dl[list[i].s], s_start) < 0) + list[i].s++; + } + /* Now that both dirlist1 and dirlist2 contain sorted directory + * listings, we can start to go through dirlist1. If both listings + * contain the same file, then do a normal diff. Otherwise, behaviour + * is determined by whether the -N flag is set. */ + while (1) { + char *dp[2]; + int pos; + int k; + + dp[0] = list[0].s < list[0].e ? list[0].dl[list[0].s] : NULL; + dp[1] = list[1].s < list[1].e ? list[1].dl[list[1].s] : NULL; + if (!dp[0] && !dp[1]) + break; + pos = !dp[0] ? 1 : (!dp[1] ? -1 : strcmp(dp[0], dp[1])); + k = pos > 0; + if (pos && !(option_mask32 & FLAG(N))) { + printf("Only in %s: %s\n", p[k], dp[k]); + exit_status |= 1; + } else { + char *fullpath[2], *path[2]; /* if -N */ + + for (i = 0; i < 2; i++) { + if (pos == 0 || i == k) { + path[i] = fullpath[i] = concat_path_file(p[i], dp[i]); + stat(fullpath[i], &stb[i]); + } else { + fullpath[i] = concat_path_file(p[i], dp[1 - i]); + path[i] = (char *)bb_dev_null; + } + } + if (pos) + stat(fullpath[k], &stb[1 - k]); + + if (S_ISDIR(stb[0].st_mode) && S_ISDIR(stb[1].st_mode)) + printf("Common subdirectories: %s and %s\n", fullpath[0], fullpath[1]); + else if (!S_ISREG(stb[0].st_mode) && !S_ISDIR(stb[0].st_mode)) + printf("File %s is not a regular file or directory and was skipped\n", fullpath[0]); + else if (!S_ISREG(stb[1].st_mode) && !S_ISDIR(stb[1].st_mode)) + printf("File %s is not a regular file or directory and was skipped\n", fullpath[1]); + else if (S_ISDIR(stb[0].st_mode) != S_ISDIR(stb[1].st_mode)) { + if (S_ISDIR(stb[0].st_mode)) + printf("File %s is a %s while file %s is a %s\n", fullpath[0], "directory", fullpath[1], "regular file"); + else + printf("File %s is a %s while file %s is a %s\n", fullpath[0], "regular file", fullpath[1], "directory"); + } else + print_status(diffreg(path), fullpath); + + free(fullpath[0]); + free(fullpath[1]); + } + free(dp[k]); + list[k].s++; + if (pos == 0) { + free(dp[1 - k]); + list[1 - k].s++; + } + } + if (ENABLE_FEATURE_CLEAN_UP) { + free(list[0].dl); + free(list[1].dl); + } +} +#endif + +#if ENABLE_FEATURE_DIFF_LONG_OPTIONS +static const char diff_longopts[] ALIGN1 = + "ignore-case\0" No_argument "i" + "ignore-tab-expansion\0" No_argument "E" + "ignore-space-change\0" No_argument "b" + "ignore-all-space\0" No_argument "w" + "ignore-blank-lines\0" No_argument "B" + "text\0" No_argument "a" + "unified\0" Required_argument "U" + "label\0" Required_argument "L" + "show-c-function\0" No_argument "p" + "brief\0" No_argument "q" + "expand-tabs\0" No_argument "t" + "initial-tab\0" No_argument "T" + "recursive\0" No_argument "r" + "new-file\0" No_argument "N" + "report-identical-files\0" No_argument "s" + "starting-file\0" Required_argument "S" + "minimal\0" No_argument "d" + ; +# define GETOPT32 getopt32long +# define LONGOPTS ,diff_longopts +#else +# define GETOPT32 getopt32 +# define LONGOPTS +#endif + +int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int diff_main(int argc UNUSED_PARAM, char **argv) +{ + int gotstdin = 0, i; + char *file[2], *s_start = NULL; + llist_t *L_arg = NULL; + + INIT_G(); + + /* exactly 2 params; collect multiple -L