From 4dc1eee385f1b5115976db8d6b60ca0a08ed05ce Mon Sep 17 00:00:00 2001 From: Ludovic Pouzenc Date: Sun, 16 Sep 2018 19:26:25 +0200 Subject: package/system: probably from OpenWRT --- package/system/ca-certificates/Makefile | 15 +- package/system/fstools/Makefile | 23 +- package/system/fstools/files/fstab.default | 2 + package/system/fstools/files/samba.hotplug | 47 ++ .../fstools/patches/001-usb-auto-mount.patch | 227 ++++++ .../system/fstools/patches/002-ntfs-exfat.patch | 525 +++++++++++++ .../system/fstools/patches/003-nand-jffs2.patch | 13 + package/system/fstools/patches/004-emmc.patch | 597 +++++++++++++++ .../system/fstools/patches/005-mount-options.patch | 102 +++ package/system/fstools/patches/006-7623-nand.patch | 819 +++++++++++++++++++++ package/system/mtd/src/mtd.c | 19 + package/system/mtd/src/trx.c | 4 +- package/system/opkg/Makefile | 2 +- package/system/procd/Makefile | 4 +- package/system/procd/files/nand.sh | 16 +- package/system/procd/files/procd.sh | 2 +- .../system/procd/patches/000-skip-watchdog.patch | 14 + package/system/ubox/Makefile | 4 +- package/system/ubox/files/log.init | 2 - package/system/ubox/patches/001-insmod.patch | 47 ++ package/system/ubus/Makefile | 4 +- 21 files changed, 2469 insertions(+), 19 deletions(-) create mode 100644 package/system/fstools/files/samba.hotplug create mode 100644 package/system/fstools/patches/001-usb-auto-mount.patch create mode 100644 package/system/fstools/patches/002-ntfs-exfat.patch create mode 100644 package/system/fstools/patches/003-nand-jffs2.patch create mode 100644 package/system/fstools/patches/004-emmc.patch create mode 100644 package/system/fstools/patches/005-mount-options.patch create mode 100644 package/system/fstools/patches/006-7623-nand.patch create mode 100644 package/system/procd/patches/000-skip-watchdog.patch create mode 100644 package/system/ubox/patches/001-insmod.patch (limited to 'package') diff --git a/package/system/ca-certificates/Makefile b/package/system/ca-certificates/Makefile index 7f38c86..0caf027 100644 --- a/package/system/ca-certificates/Makefile +++ b/package/system/ca-certificates/Makefile @@ -7,12 +7,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ca-certificates -PKG_VERSION:=20140325 +PKG_VERSION:=20141019 PKG_SOURCE:=$(PKG_NAME)_$(PKG_VERSION).tar.xz -PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) PKG_SOURCE_URL:=http://ftp.debian.org/debian/pool/main/c/ca-certificates -PKG_MD5SUM:=0436aba482091da310bd762e1deca8b4 +PKG_MD5SUM:=f619282081c8bfc65ea64c37fa5285ed PKG_INSTALL:=1 @@ -34,6 +34,15 @@ endef define Package/ca-certificates/install $(INSTALL_DIR) $(1)/etc/ssl/certs $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/ca-certificates/*/*.crt $(1)/etc/ssl/certs/ + + for CERTFILE in `ls -1 $(1)/etc/ssl/certs`; do \ + HASH=`openssl x509 -hash -noout -in $(1)/etc/ssl/certs/$$$$CERTFILE` ; \ + SUFFIX=0 ; \ + while [ -h "$(1)/etc/ssl/certs/$$$$HASH.$$$$SUFFIX" ]; do \ + let "SUFFIX += 1" ; \ + done ; \ + ln -s "$$$$CERTFILE" "$(1)/etc/ssl/certs/$$$$HASH.$$$$SUFFIX" ; \ + done endef $(eval $(call BuildPackage,ca-certificates)) diff --git a/package/system/fstools/Makefile b/package/system/fstools/Makefile index 18098dd..7e93ec9 100644 --- a/package/system/fstools/Makefile +++ b/package/system/fstools/Makefile @@ -1,5 +1,5 @@ # -# Copyright (C) 2014 OpenWrt.org +# Copyright (C) 2014-2015 OpenWrt.org # # This is free software, licensed under the GNU General Public License v2. # See /LICENSE for more information. @@ -8,14 +8,14 @@ include $(TOPDIR)/rules.mk PKG_NAME:=fstools -PKG_VERSION:=2014-06-22 +PKG_VERSION:=2015-02-25.1 PKG_RELEASE=$(PKG_SOURCE_VERSION) PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=git://nbd.name/fstools.git PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) -PKG_SOURCE_VERSION:=e0430f5c62f367e5a8e02755412977b02c3fc45e +PKG_SOURCE_VERSION:=914b023e71559e033ec5a1f9840511eb1ccaf386 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz CMAKE_INSTALL:=1 PKG_CHECK_FORMAT_SECURITY:=0 @@ -23,18 +23,32 @@ PKG_CHECK_FORMAT_SECURITY:=0 PKG_LICENSE:=GPLv2 PKG_LICENSE_FILES:= +PKG_USE_MIPS16:=0 + PKG_MAINTAINER:=John Crispin include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/cmake.mk TARGET_LDFLAGS += $(if $(CONFIG_USE_EGLIBC),-lrt) +CMAKE_OPTIONS += $(if $(CONFIG_FSTOOLS_UBIFS_EXTROOT),-DCMAKE_UBIFS_EXTROOT=y) define Package/fstools SECTION:=base CATEGORY:=Base system DEPENDS:=+ubox +USE_EGLIBC:librt +NAND_SUPPORT:ubi-utils TITLE:=OpenWrt filesystem tools + MENU:=1 +endef + +define Package/fstools/config + config FSTOOLS_UBIFS_EXTROOT + depends on PACKAGE_fstools + depends on NAND_SUPPORT + bool "Support extroot functionality with UBIFS" + default y + help + This option makes it possible to use extroot functionality if the root filesystem resides on an UBIFS partition endef define Package/block-mount @@ -47,7 +61,7 @@ endef define Package/fstools/install $(INSTALL_DIR) $(1)/sbin $(1)/lib - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/{mount_root,jffs2reset,snapshot_tool} $(1)/sbin/ + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/{mount_root,mount_root2,jffs2reset,snapshot_tool} $(1)/sbin/ $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libfstools.so $(1)/lib/ $(INSTALL_BIN) ./files/snapshot $(1)/sbin/ ln -s /sbin/jffs2reset $(1)/sbin/jffs2mark @@ -59,6 +73,7 @@ define Package/block-mount/install $(INSTALL_BIN) ./files/fstab.init $(1)/etc/init.d/fstab $(INSTALL_DATA) ./files/fstab.default $(1)/etc/uci-defaults/10-fstab $(INSTALL_DATA) ./files/mount.hotplug $(1)/etc/hotplug.d/block/10-mount + -$(INSTALL_DATA) ./files/samba.hotplug $(1)/etc/hotplug.d/block/11-samba $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/block $(1)/sbin/ $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libblkid-tiny.so $(1)/lib/ diff --git a/package/system/fstools/files/fstab.default b/package/system/fstools/files/fstab.default index dd4ba1c..f794bb9 100644 --- a/package/system/fstools/files/fstab.default +++ b/package/system/fstools/files/fstab.default @@ -1 +1,3 @@ +touch /tmp/fstab [ ! -f /etc/config/fstab ] && ( block detect > /etc/config/fstab ) +exit 0 diff --git a/package/system/fstools/files/samba.hotplug b/package/system/fstools/files/samba.hotplug new file mode 100644 index 0000000..270e32c --- /dev/null +++ b/package/system/fstools/files/samba.hotplug @@ -0,0 +1,47 @@ +logger $ACTION $DEVNAME $DEVTYPE + +[ "$ACTION" = "add" ] && { + [ "$DEVTYPE" = "partition" -o "$DEVTYPE" = "disk" ] && { + [ "${DEVNAME:0:2}" = "sd" ] && { + [ "$(mount | grep $DEVNAME | cut -d ' ' -f1 | tr -d '\n')" = "/dev/$DEVNAME" ] && { + section=`/sbin/uci add samba sambashare` + /sbin/uci rename samba.$section=usb$DEVNAME + /sbin/uci set samba.usb$DEVNAME.name=usb$DEVNAME + /sbin/uci set samba.usb$DEVNAME.path=/mnt/$DEVNAME + /sbin/uci set samba.usb$DEVNAME.read_only=no + /sbin/uci set samba.usb$DEVNAME.guest_ok=yes + /sbin/uci set samba.usb$DEVNAME.create_mask=777 + /sbin/uci set samba.usb$DEVNAME.dir_mask=777 + /sbin/uci commit + chmod 777 /mnt/$DEVNAME + + touch /tmp/smb.flag + smp.sh storage + + /etc/init.d/samba restart + } + } + } +} + +[ "$ACTION" = "remove" ] && { + [ "$DEVTYPE" = "partition" -o "$DEVTYPE" = "disk" ] && { + [ "${DEVNAME:0:2}" = "sd" ] && { + [ `/sbin/uci get samba.usb$DEVNAME.name` = "" ] && break + [ `/sbin/uci get samba.usb$DEVNAME.name` = "usb$DEVNAME" ] && { + /sbin/uci delete samba.usb$DEVNAME + /sbin/uci commit + } + ret=$(/sbin/uci get samba.@sambashare[0] 2>&1 | awk '{print $3}' | tr -d '\n') + [ $ret = "not" ] && { + /etc/init.d/samba stop + rm -f /tmp/smb.flag + } + [ $ret = "not" ] || { + /etc/init.d/samba restart + } + } + } +} + + diff --git a/package/system/fstools/patches/001-usb-auto-mount.patch b/package/system/fstools/patches/001-usb-auto-mount.patch new file mode 100644 index 0000000..1ad0c9c --- /dev/null +++ b/package/system/fstools/patches/001-usb-auto-mount.patch @@ -0,0 +1,227 @@ +Index: fstools-2015-02-25.1/block.c +=================================================================== +--- fstools-2015-02-25.1.orig/block.c ++++ fstools-2015-02-25.1/block.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -279,7 +280,7 @@ static int mount_add(struct uci_section + struct blob_attr *tb[__MOUNT_MAX] = { 0 }; + struct mount *m; + +- blob_buf_init(&b, 0); ++ blob_buf_init(&b, 0); + uci_to_blob(&b, s, &mount_attr_list); + blobmsg_parse(mount_policy, __MOUNT_MAX, tb, blob_data(b.head), blob_len(b.head)); + +@@ -319,7 +320,7 @@ static int swap_add(struct uci_section * + struct blob_attr *tb[__SWAP_MAX] = { 0 }; + struct mount *m; + +- blob_buf_init(&b, 0); ++ blob_buf_init(&b, 0); + uci_to_blob(&b, s, &swap_attr_list); + blobmsg_parse(swap_policy, __SWAP_MAX, tb, blob_data(b.head), blob_len(b.head)); + +@@ -357,7 +358,7 @@ static int global_add(struct uci_section + { + struct blob_attr *tb[__CFG_MAX] = { 0 }; + +- blob_buf_init(&b, 0); ++ blob_buf_init(&b, 0); + uci_to_blob(&b, s, &config_attr_list); + blobmsg_parse(config_policy, __CFG_MAX, tb, blob_data(b.head), blob_len(b.head)); + +@@ -414,6 +415,13 @@ static struct mount* find_block(const ch + return m; + if (m->device && device && !strcmp(m->device, device)) + return m; ++ /* try wildcard match */ ++ if (m->device && device && strchr(m->device, '*')) ++ { ++ char * p = strchr(m->device, '*'); ++ if (0 == strncmp(device, m->device, p - m->device)) ++ return m; ++ } + } + + return NULL; +@@ -745,6 +753,7 @@ static int mount_device(struct blkid_str + char *target = m->target; + char _target[32]; + int err = 0; ++ uint32_t mflags = m->flags; + + if (!target) { + snprintf(_target, sizeof(_target), "/mnt/%s", device); +@@ -755,19 +764,34 @@ static int mount_device(struct blkid_str + if (check_fs) + check_filesystem(pr); + +- err = mount(pr->dev, target, pr->id->name, m->flags, +- (m->options) ? (m->options) : ("")); +- if (err) +- ERROR("mounting %s (%s) as %s failed (%d) - %s\n", +- pr->dev, pr->id->name, target, err, strerror(err)); +- else +- handle_swapfiles(true); ++ while (1) { ++ err = mount(pr->dev, target, pr->id->name, mflags, ++ (m->options) ? (m->options) : ("")); ++ if (err) { ++ if (!(mflags & MS_RDONLY)) ++ { ++ ERROR("Fall back on RO mount. %s.\n", strerror(errno)); ++ mflags |= MS_RDONLY; ++ continue; ++ } ++ else { ++ ERROR("mounting %s (%s) as %s failed (%d) - %s\n", ++ pr->dev, pr->id->name, target, err, strerror(errno)); ++ break; ++ } ++ } ++ else { ++ handle_swapfiles(true); ++ break; ++ } ++ } + return err; + } + + if (anon_mount) { + char target[] = "/mnt/mmcblk123"; + int err = 0; ++ uint32_t mflags = 0; + + snprintf(target, sizeof(target), "/mnt/%s", device); + mkdir_p(target); +@@ -775,12 +799,25 @@ static int mount_device(struct blkid_str + if (check_fs) + check_filesystem(pr); + +- err = mount(pr->dev, target, pr->id->name, 0, ""); +- if (err) +- ERROR("mounting %s (%s) as %s failed (%d) - %s\n", +- pr->dev, pr->id->name, target, err, strerror(err)); +- else +- handle_swapfiles(true); ++ while (1) { ++ err = mount(pr->dev, target, pr->id->name, mflags, ""); ++ if (err) { ++ if (!(mflags & MS_RDONLY)) { ++ ERROR("Fall back on RO mount. %s.\n", strerror(errno)); ++ mflags |= MS_RDONLY; ++ continue; ++ } ++ else { ++ ERROR("mounting %s (%s) as %s failed (%d) - %s\n", ++ pr->dev, pr->id->name, target, err, strerror(errno)); ++ break; ++ } ++ } ++ else { ++ handle_swapfiles(true); ++ break; ++ } ++ } + return err; + } + +@@ -811,7 +848,7 @@ static int umount_device(struct blkid_st + err = umount2(mp, MNT_DETACH); + if (err) + ERROR("unmounting %s (%s) failed (%d) - %s\n", +- pr->dev, mp, err, strerror(err)); ++ pr->dev, mp, err, strerror(errno)); + else + ERROR("unmounted %s (%s)\n", + pr->dev, mp); +@@ -839,7 +876,7 @@ static int main_hotplug(int argc, char * + + if (err) + ERROR("umount of %s failed (%d) - %s\n", +- mount_point, err, strerror(err)); ++ mount_point, err, strerror(errno)); + + return 0; + } else if (strcmp(action, "add")) { +@@ -1034,7 +1071,7 @@ static int check_extroot(char *path) + fp = fopen(tag, "w+"); + if (!fp) { + ERROR("extroot: failed to write UUID to %s: %d (%s)\n", +- tag, errno, strerror(errno)); ++ tag, errno, strerror(errno)); + /* return 0 to continue boot regardless of error */ + return 0; + } +@@ -1046,7 +1083,7 @@ static int check_extroot(char *path) + fp = fopen(tag, "r"); + if (!fp) { + ERROR("extroot: failed to read UUID from %s: %d (%s)\n", +- tag, errno, strerror(errno)); ++ tag, errno, strerror(errno)); + return -1; + } + +@@ -1057,7 +1094,7 @@ static int check_extroot(char *path) + return 0; + + ERROR("extroot: UUID mismatch (root: %s, %s: %s)\n", +- pr->uuid, basename(path), uuid); ++ pr->uuid, basename(path), uuid); + return -1; + } + } +@@ -1105,7 +1142,7 @@ static int mount_extroot(char *cfg) + } + if (pr) { + if (strncmp(pr->id->name, "ext", 3) && +- strncmp(pr->id->name, "ubifs", 5)) { ++ strncmp(pr->id->name, "ubifs", 5)) { + ERROR("extroot: unsupported filesystem %s, try ext4\n", pr->id->name); + return -1; + } +@@ -1126,7 +1163,7 @@ static int mount_extroot(char *cfg) + + if (err) { + ERROR("extroot: mounting %s (%s) on %s failed: %d (%s)\n", +- pr->dev, pr->id->name, path, err, strerror(err)); ++ pr->dev, pr->id->name, path, err, strerror(errno)); + } else if (m->overlay) { + err = check_extroot(path); + if (err) +@@ -1134,8 +1171,8 @@ static int mount_extroot(char *cfg) + } + } else { + ERROR("extroot: cannot find device %s%s\n", +- (m->uuid ? "with UUID " : (m->label ? "with label " : "")), +- (m->uuid ? m->uuid : (m->label ? m->label : m->device))); ++ (m->uuid ? "with UUID " : (m->label ? "with label " : "")), ++ (m->uuid ? m->uuid : (m->label ? m->label : m->device))); + } + + return err; +@@ -1210,7 +1247,7 @@ static int main_extroot(int argc, char * + rmdir("/tmp/overlay"); + rmdir(cfg); + return err; +- } ++ } + #endif + + return mount_extroot(NULL); +@@ -1255,7 +1292,7 @@ static int main_detect(int argc, char ** + cache_load(0); + printf("config 'global'\n"); + printf("\toption\tanon_swap\t'0'\n"); +- printf("\toption\tanon_mount\t'0'\n"); ++ printf("\toption\tanon_mount\t'1'\n"); + printf("\toption\tauto_swap\t'1'\n"); + printf("\toption\tauto_mount\t'1'\n"); + printf("\toption\tdelay_root\t'5'\n"); diff --git a/package/system/fstools/patches/002-ntfs-exfat.patch b/package/system/fstools/patches/002-ntfs-exfat.patch new file mode 100644 index 0000000..3d8c7e5 --- /dev/null +++ b/package/system/fstools/patches/002-ntfs-exfat.patch @@ -0,0 +1,525 @@ +Index: fstools-2015-02-25.1/CMakeLists.txt +=================================================================== +--- fstools-2015-02-25.1.orig/CMakeLists.txt ++++ fstools-2015-02-25.1/CMakeLists.txt +@@ -23,6 +23,8 @@ ADD_LIBRARY(blkid-tiny SHARED + libblkid-tiny/ext.c + libblkid-tiny/jffs2.c + libblkid-tiny/vfat.c ++ libblkid-tiny/ntfs.c ++ libblkid-tiny/exfat.c + libblkid-tiny/hfs.c + libblkid-tiny/swap.c + libblkid-tiny/ubifs.c +Index: fstools-2015-02-25.1/libblkid-tiny/libblkid-tiny.c +=================================================================== +--- fstools-2015-02-25.1.orig/libblkid-tiny/libblkid-tiny.c ++++ fstools-2015-02-25.1/libblkid-tiny/libblkid-tiny.c +@@ -10,6 +10,8 @@ + #define DEBUG(fmt, ...) + #endif + ++#define isspace(x) ((x == ' ') || (x == '\t') || (x == '\r') || (x == '\n')) ++ + int blkid_debug_mask = 0; + + static unsigned char *probe_buffer; +@@ -152,9 +154,77 @@ int blkid_probe_sprintf_uuid(blkid_probe + return 0; + } + ++size_t blkid_encode_to_utf8(int enc, unsigned char *dest, size_t len, ++ const unsigned char *src, size_t count) ++{ ++ size_t i, j; ++ uint16_t c; ++ ++ for (j = i = 0; i + 2 <= count; i += 2) ++ { ++ if (enc == BLKID_ENC_UTF16LE) ++ c = (src[i+1] << 8) | src[i]; ++ else /* BLKID_ENC_UTF16BE */ ++ c = (src[i] << 8) | src[i+1]; ++ if (c == 0) ++ { ++ dest[j] = '\0'; ++ break; ++ } ++ else if (c < 0x80) ++ { ++ if (j+1 >= len) ++ break; ++ dest[j++] = (uint8_t) c; ++ } ++ else if (c < 0x800) ++ { ++ if (j+2 >= len) ++ break; ++ dest[j++] = (uint8_t) (0xc0 | (c >> 6)); ++ dest[j++] = (uint8_t) (0x80 | (c & 0x3f)); ++ } ++ else ++ { ++ if (j+3 >= len) ++ break; ++ dest[j++] = (uint8_t) (0xe0 | (c >> 12)); ++ dest[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f)); ++ dest[j++] = (uint8_t) (0x80 | (c & 0x3f)); ++ } ++ } ++ dest[j] = '\0'; ++ return j; ++} ++ ++size_t blkid_rtrim_whitespace(unsigned char *str) ++{ ++ size_t i = strlen((char *) str); ++ ++ while (i--) ++ { ++ if (!isspace(str[i])) ++ break; ++ } ++ str[++i] = '\0'; ++ return i; ++} ++ ++int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label, ++ size_t len, int enc) ++{ ++ blkid_encode_to_utf8(enc, (unsigned char *) pr->label, sizeof(pr->label), label, len); ++ blkid_rtrim_whitespace((unsigned char *) pr->label); ++ return 0; ++} ++ ++ ++ + static const struct blkid_idinfo *idinfos[] = + { + &vfat_idinfo, ++ &ntfs_idinfo, ++ &exfat_idinfo, + &swsuspend_idinfo, + &swap_idinfo, + &ext4dev_idinfo, +Index: fstools-2015-02-25.1/libblkid-tiny/ntfs.c +=================================================================== +--- /dev/null ++++ fstools-2015-02-25.1/libblkid-tiny/ntfs.c +@@ -0,0 +1,246 @@ ++/* ++ * Copyright (C) 2004 Kay Sievers ++ * Copyright (C) 2008 Karel Zak ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "superblocks.h" ++ ++struct ntfs_bios_parameters ++{ ++ uint16_t sector_size; /* Size of a sector in bytes. */ ++ uint8_t sectors_per_cluster; /* Size of a cluster in sectors. */ ++ uint16_t reserved_sectors; /* zero */ ++ uint8_t fats; /* zero */ ++ uint16_t root_entries; /* zero */ ++ uint16_t sectors; /* zero */ ++ uint8_t media_type; /* 0xf8 = hard disk */ ++ uint16_t sectors_per_fat; /* zero */ ++ uint16_t sectors_per_track; /* irrelevant */ ++ uint16_t heads; /* irrelevant */ ++ uint32_t hidden_sectors; /* zero */ ++ uint32_t large_sectors; /* zero */ ++} __attribute__ ((__packed__)); ++ ++struct ntfs_super_block ++{ ++ uint8_t jump[3]; ++ uint8_t oem_id[8]; /* magic string */ ++ ++ struct ntfs_bios_parameters bpb; ++ ++ uint16_t unused[2]; ++ uint64_t number_of_sectors; ++ uint64_t mft_cluster_location; ++ uint64_t mft_mirror_cluster_location; ++ int8_t clusters_per_mft_record; ++ uint8_t reserved1[3]; ++ int8_t cluster_per_index_record; ++ uint8_t reserved2[3]; ++ uint64_t volume_serial; ++ uint32_t checksum; ++} __attribute__((packed)); ++ ++struct master_file_table_record ++{ ++ uint32_t magic; ++ uint16_t usa_ofs; ++ uint16_t usa_count; ++ uint64_t lsn; ++ uint16_t sequence_number; ++ uint16_t link_count; ++ uint16_t attrs_offset; ++ uint16_t flags; ++ uint32_t bytes_in_use; ++ uint32_t bytes_allocated; ++} __attribute__((__packed__)); ++ ++struct file_attribute ++{ ++ uint32_t type; ++ uint32_t len; ++ uint8_t non_resident; ++ uint8_t name_len; ++ uint16_t name_offset; ++ uint16_t flags; ++ uint16_t instance; ++ uint32_t value_len; ++ uint16_t value_offset; ++} __attribute__((__packed__)); ++ ++#define MFT_RECORD_VOLUME 3 ++#define NTFS_MAX_CLUSTER_SIZE (64 * 1024) ++ ++enum ++{ ++ MFT_RECORD_ATTR_VOLUME_NAME = 0x60, ++ MFT_RECORD_ATTR_END = 0xffffffff ++}; ++ ++static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag) ++{ ++ struct ntfs_super_block *ns; ++ struct master_file_table_record *mft; ++ ++ uint32_t sectors_per_cluster, mft_record_size, attr_off; ++ uint16_t sector_size; ++ uint64_t nr_clusters, off; ++ unsigned char *buf_mft; ++ ++ ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block); ++ if (!ns) ++ return -1; ++ ++ /* ++ * Check bios parameters block ++ */ ++ sector_size = le16_to_cpu(ns->bpb.sector_size); ++ sectors_per_cluster = ns->bpb.sectors_per_cluster; ++ ++ if (sector_size < 256 || sector_size > 4096) ++ return 1; ++ ++ switch (sectors_per_cluster) ++ { ++ case 1: ++ case 2: ++ case 4: ++ case 8: ++ case 16: ++ case 32: ++ case 64: ++ case 128: ++ break; ++ default: ++ return 1; ++ } ++ ++ if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) * ++ ns->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE) ++ return 1; ++ ++ /* Unused fields must be zero */ ++ if (le16_to_cpu(ns->bpb.reserved_sectors) ++ || le16_to_cpu(ns->bpb.root_entries) ++ || le16_to_cpu(ns->bpb.sectors) ++ || le16_to_cpu(ns->bpb.sectors_per_fat) ++ || le32_to_cpu(ns->bpb.large_sectors) ++ || ns->bpb.fats) ++ return 1; ++ ++ if ((uint8_t) ns->clusters_per_mft_record < 0xe1 ++ || (uint8_t) ns->clusters_per_mft_record > 0xf7) ++ { ++ ++ switch (ns->clusters_per_mft_record) ++ { ++ case 1: ++ case 2: ++ case 4: ++ case 8: ++ case 16: ++ case 32: ++ case 64: ++ break; ++ default: ++ return 1; ++ } ++ } ++ ++ if (ns->clusters_per_mft_record > 0) ++ mft_record_size = ns->clusters_per_mft_record * ++ sectors_per_cluster * sector_size; ++ else ++ mft_record_size = 1 << (0 - ns->clusters_per_mft_record); ++ ++ nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster; ++ ++ if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) || ++ (le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters)) ++ return 1; ++ ++ ++ off = le64_to_cpu(ns->mft_cluster_location) * sector_size * ++ sectors_per_cluster; ++ ++ DBG(DEBUG_LOWPROBE, printf("NTFS: sector_size=%d, mft_record_size=%d, " ++ "sectors_per_cluster=%d, nr_clusters=%ju " ++ "cluster_offset=%jd", ++ (int) sector_size, mft_record_size, ++ sectors_per_cluster, nr_clusters, ++ off)); ++ ++ buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); ++ if (!buf_mft) ++ return 1; ++ ++ if (memcmp(buf_mft, "FILE", 4)) ++ return 1; ++ ++ off += MFT_RECORD_VOLUME * mft_record_size; ++ ++ buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); ++ if (!buf_mft) ++ return 1; ++ ++ if (memcmp(buf_mft, "FILE", 4)) ++ return 1; ++ ++ mft = (struct master_file_table_record *) buf_mft; ++ attr_off = le16_to_cpu(mft->attrs_offset); ++ ++ while (attr_off < mft_record_size && ++ attr_off <= le32_to_cpu(mft->bytes_allocated)) ++ { ++ ++ uint32_t attr_len; ++ struct file_attribute *attr; ++ ++ attr = (struct file_attribute *) (buf_mft + attr_off); ++ attr_len = le32_to_cpu(attr->len); ++ if (!attr_len) ++ break; ++ ++ if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_END) ++ break; ++ if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_VOLUME_NAME) ++ { ++ unsigned int val_off = le16_to_cpu(attr->value_offset); ++ unsigned int val_len = le32_to_cpu(attr->value_len); ++ unsigned char *val = ((uint8_t *) attr) + val_off; ++ ++ blkid_probe_set_utf8label(pr, val, val_len, BLKID_ENC_UTF16LE); ++ break; ++ } ++ ++ if (UINT_MAX - attr_len < attr_off) ++ break; ++ attr_off += attr_len; ++ } ++ ++ blkid_probe_sprintf_uuid(pr, ++ (unsigned char *) &ns->volume_serial, ++ sizeof(ns->volume_serial), ++ "%016" PRIX64, le64_to_cpu(ns->volume_serial)); ++ return 0; ++} ++ ++ ++const struct blkid_idinfo ntfs_idinfo = ++{ ++ .name = "ntfs", ++ .usage = BLKID_USAGE_FILESYSTEM, ++ .probefunc = probe_ntfs, ++ .magics = ++ { ++ { .magic = "NTFS ", .len = 5, .sboff = 3 }, ++ { NULL } ++ } ++}; +Index: fstools-2015-02-25.1/libblkid-tiny/exfat.c +=================================================================== +--- /dev/null ++++ fstools-2015-02-25.1/libblkid-tiny/exfat.c +@@ -0,0 +1,151 @@ ++/* ++ * Copyright (C) 2010 Andrew Nayenko ++ * ++ * This file may be redistributed under the terms of the ++ * GNU Lesser General Public License. ++ */ ++#include "superblocks.h" ++ ++struct exfat_super_block ++{ ++ uint8_t jump[3]; ++ uint8_t oem_name[8]; ++ uint8_t __unused1[53]; ++ uint64_t block_start; ++ uint64_t block_count; ++ uint32_t fat_block_start; ++ uint32_t fat_block_count; ++ uint32_t cluster_block_start; ++ uint32_t cluster_count; ++ uint32_t rootdir_cluster; ++ uint8_t volume_serial[4]; ++ struct ++ { ++ uint8_t minor; ++ uint8_t major; ++ } version; ++ uint16_t volume_state; ++ uint8_t block_bits; ++ uint8_t bpc_bits; ++ uint8_t fat_count; ++ uint8_t drive_no; ++ uint8_t allocated_percent; ++} __attribute__((__packed__)); ++ ++struct exfat_entry_label ++{ ++ uint8_t type; ++ uint8_t length; ++ uint8_t name[30]; ++} __attribute__((__packed__)); ++ ++#define BLOCK_SIZE(sb) (1 << (sb)->block_bits) ++#define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits) ++#define EXFAT_FIRST_DATA_CLUSTER 2 ++#define EXFAT_LAST_DATA_CLUSTER 0xffffff6 ++#define EXFAT_ENTRY_SIZE 32 ++ ++#define EXFAT_ENTRY_EOD 0x00 ++#define EXFAT_ENTRY_LABEL 0x83 ++ ++static blkid_loff_t block_to_offset(const struct exfat_super_block *sb, ++ blkid_loff_t block) ++{ ++ return (blkid_loff_t) block << sb->block_bits; ++} ++ ++static blkid_loff_t cluster_to_block(const struct exfat_super_block *sb, ++ uint32_t cluster) ++{ ++ return le32_to_cpu(sb->cluster_block_start) + ++ ((blkid_loff_t) (cluster - EXFAT_FIRST_DATA_CLUSTER) ++ << sb->bpc_bits); ++} ++ ++static blkid_loff_t cluster_to_offset(const struct exfat_super_block *sb, ++ uint32_t cluster) ++{ ++ return block_to_offset(sb, cluster_to_block(sb, cluster)); ++} ++ ++static uint32_t next_cluster(blkid_probe pr, ++ const struct exfat_super_block *sb, uint32_t cluster) ++{ ++ uint32_t *next; ++ blkid_loff_t fat_offset; ++ ++ fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start)) ++ + (blkid_loff_t) cluster * sizeof(cluster); ++ next = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset, ++ sizeof(uint32_t)); ++ if (!next) ++ return 0; ++ return le32_to_cpu(*next); ++} ++ ++static struct exfat_entry_label *find_label(blkid_probe pr, ++ const struct exfat_super_block *sb) ++{ ++ uint32_t cluster = le32_to_cpu(sb->rootdir_cluster); ++ blkid_loff_t offset = cluster_to_offset(sb, cluster); ++ uint8_t *entry; ++ ++ for (;;) ++ { ++ entry = (uint8_t *) blkid_probe_get_buffer(pr, offset, ++ EXFAT_ENTRY_SIZE); ++ if (!entry) ++ return NULL; ++ if (entry[0] == EXFAT_ENTRY_EOD) ++ return NULL; ++ if (entry[0] == EXFAT_ENTRY_LABEL) ++ return (struct exfat_entry_label *) entry; ++ offset += EXFAT_ENTRY_SIZE; ++ if (offset % CLUSTER_SIZE(sb) == 0) ++ { ++ cluster = next_cluster(pr, sb, cluster); ++ if (cluster < EXFAT_FIRST_DATA_CLUSTER) ++ return NULL; ++ if (cluster > EXFAT_LAST_DATA_CLUSTER) ++ return NULL; ++ offset = cluster_to_offset(sb, cluster); ++ } ++ } ++} ++ ++static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag) ++{ ++ struct exfat_super_block *sb; ++ struct exfat_entry_label *label; ++ ++ sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block); ++ if (!sb) ++ return -1; ++ ++ blkid_probe_sprintf_uuid(pr, sb->volume_serial, 4, ++ "%02hhX%02hhX-%02hhX%02hhX", ++ sb->volume_serial[3], sb->volume_serial[2], ++ sb->volume_serial[1], sb->volume_serial[0]); ++ ++ blkid_probe_sprintf_version(pr, "%u.%u", ++ sb->version.major, sb->version.minor); ++ ++ label = find_label(pr, sb); ++ if (label) ++ blkid_probe_set_utf8label(pr, label->name, ++ min(label->length * 2, 30), BLKID_ENC_UTF16LE); ++ ++ return 0; ++} ++ ++const struct blkid_idinfo exfat_idinfo = ++{ ++ .name = "exfat", ++ .usage = BLKID_USAGE_FILESYSTEM, ++ .probefunc = probe_exfat, ++ .magics = ++ { ++ { .magic = "EXFAT ", .len = 8, .sboff = 3 }, ++ { NULL } ++ } ++}; +Index: fstools-2015-02-25.1/libblkid-tiny/superblocks.h +=================================================================== +--- fstools-2015-02-25.1.orig/libblkid-tiny/superblocks.h ++++ fstools-2015-02-25.1/libblkid-tiny/superblocks.h +@@ -87,6 +87,8 @@ extern int blkid_probe_strncpy_uuid(blki + + extern int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid); + extern int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name); +- ++extern size_t blkid_encode_to_utf8(int enc, unsigned char *dest, size_t len, ++ const unsigned char *src, size_t count); ++ + + #endif /* _BLKID_SUPERBLOCKS_H */ diff --git a/package/system/fstools/patches/003-nand-jffs2.patch b/package/system/fstools/patches/003-nand-jffs2.patch new file mode 100644 index 0000000..17c5afa --- /dev/null +++ b/package/system/fstools/patches/003-nand-jffs2.patch @@ -0,0 +1,13 @@ +Index: fstools-2015-02-25.1/libfstools/mtd.c +=================================================================== +--- fstools-2015-02-25.1.orig/libfstools/mtd.c ++++ fstools-2015-02-25.1/libfstools/mtd.c +@@ -214,7 +214,7 @@ static int mtd_volume_identify(struct vo + return FS_JFFS2; + } + +- if (v->type == UBIVOLUME && deadc0de == 0xffffffff) { ++ if ((v->type == UBIVOLUME || v->type == NANDFLASH) && deadc0de == 0xffffffff) { + fprintf(stderr, "jffs2 is ready\n"); + return FS_JFFS2; + } diff --git a/package/system/fstools/patches/004-emmc.patch b/package/system/fstools/patches/004-emmc.patch new file mode 100644 index 0000000..86b8ca7 --- /dev/null +++ b/package/system/fstools/patches/004-emmc.patch @@ -0,0 +1,597 @@ +Index: fstools-2015-02-25.1/mount_root.c +=================================================================== +--- fstools-2015-02-25.1.orig/mount_root.c ++++ fstools-2015-02-25.1/mount_root.c +@@ -62,6 +62,7 @@ start(int argc, char *argv[1]) + + case FS_JFFS2: + case FS_UBIFS: ++ case FS_EXT4: + mount_overlay(data); + break; + +@@ -89,6 +90,7 @@ static int + done(int argc, char *argv[1]) + { + struct volume *v = volume_find("rootfs_data"); ++ FILE * fp = NULL; + + if (!v) + return -1; +@@ -96,6 +98,19 @@ done(int argc, char *argv[1]) + switch (volume_identify(v)) { + case FS_NONE: + case FS_DEADCODE: ++ /* if the mtd is created by block2mtd */ ++ fp = fopen("/proc/cmdline", "rb"); ++ if (fp) { ++ char buf[2048] = {0}; ++ fread(buf, 1, sizeof(buf), fp); ++ fclose(fp); ++ buf[sizeof(buf)-1] = 0; ++ if (strstr(buf, "block2mtd")) { ++ fprintf(stderr, "%s is created by block2mtd, ext4_switch.\n", v->name); ++ return ext4_switch(v); ++ } ++ } ++ fprintf(stderr, "%s, normally, jffs2_switch.\n", v->name); + return jffs2_switch(v); + } + +@@ -104,8 +119,11 @@ done(int argc, char *argv[1]) + + int main(int argc, char **argv) + { +- if (argc < 2) ++ if (argc < 2) { ++ fprintf(stderr, "%s.\n", argv[0]); + return start(argc, argv); ++ } ++ fprintf(stderr, "%s %s.\n", argv[0], argv[1]); + if (!strcmp(argv[1], "stop")) + return stop(argc, argv); + if (!strcmp(argv[1], "done")) +Index: fstools-2015-02-25.1/libfstools/emmc.c +=================================================================== +--- /dev/null ++++ fstools-2015-02-25.1/libfstools/emmc.c +@@ -0,0 +1,272 @@ ++/* ++ * Copyright (C) 2015 Hua Shao ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "libfstools.h" ++ ++#include "volume.h" ++ ++#define PATH_MAX 256 ++ ++struct emmc_priv { ++ int fd; ++ int idx; ++ char *chr; ++}; ++ ++static struct driver emmc_driver; ++ ++ ++static int emmc_partition_load(struct volume *v) ++{ ++ struct emmc_priv *p = (struct emmc_priv*) v->priv; ++ struct mtd_info_user mtd_info; ++ ++ fprintf(stderr, "%s(%s)\n", __FUNCTION__, v->name); ++ if (p->fd) ++ return 0; ++ ++ p->fd = open(p->chr, O_RDWR | O_SYNC); ++ if (p->fd < 0) { ++ p->fd = 0; ++ fprintf(stderr, "Could not open block2mtd device: %s\n", p->chr); ++ return -1; ++ } ++ ++ v->type = EMMC; ++ ++ if (ioctl(p->fd, MEMGETINFO, &mtd_info)) { ++ close(p->fd); ++ fprintf(stderr, "Could not get MTD device info from %s(block2mtd)\n", p->chr); ++ v->size = 0; // TODO ++ v->block_size = -1; // TODO ++ } ++ ++ v->size = mtd_info.size; ++ v->block_size = mtd_info.erasesize; ++ ++ return 0; ++} ++ ++ ++static int block2mtd_find_index(char *name) ++{ ++ FILE *fp = fopen("/proc/mtd", "r"); ++ static char line[256]; ++ char *index = NULL; ++ ++ if(!fp) ++ return -1; ++ ++ while (!index && fgets(line, sizeof(line), fp)) { ++ char *ret; ++ ++ if ((ret = strstr(line, name)) && (ret[strlen(name)] == '"')) { ++ char *eol = strstr(line, ":"); ++ ++ if (!eol) ++ continue; ++ ++ *eol = '\0'; ++ index = &line[3]; ++ } ++ } ++ ++ fclose(fp); ++ if (index) ++ return atoi(index); ++ else ++ return -1; ++} ++ ++ ++static int emmc_partition_find(struct volume *v, char *name) ++{ ++ struct emmc_priv *p; ++ static char buffer[32]; ++ FILE * fp = NULL; ++ ++ fprintf(stderr, "%s(%p, %s)\n", __FUNCTION__, v, name); ++ ++ fp = fopen("/proc/cmdline", "rb"); ++ if (fp) { ++ char buf[2048] = {0}; ++ fread(buf, 1, sizeof(buf), fp); ++ fclose(fp); ++ buf[sizeof(buf)-1] = 0; ++ if (!strstr(buf, "block2mtd")) { ++ fprintf(stderr, "%s is not created by block2mtd, skip.\n", v->name); ++ return -1; ++ } ++ } ++ ++ p = calloc(1, sizeof(struct emmc_priv)); ++ if (!p) ++ return -1; ++ ++ p->idx = block2mtd_find_index(name); ++ ++ v->priv = p; ++ v->name = strdup(name); ++ v->drv = &emmc_driver; ++ ++ snprintf(buffer, sizeof(buffer), "/dev/mtdblock%d", p->idx); ++ v->blk = strdup(buffer); ++ ++ snprintf(buffer, sizeof(buffer), "/dev/mtd%d", p->idx); ++ p->chr = strdup(buffer); ++ ++ if (emmc_partition_load(v)) { ++ fprintf(stderr, "%s emmc_partition_load %s failed\n", __FUNCTION__, v->name); ++ return -1; ++ } ++ ++ fprintf(stderr, "v->name = %s\n", v->name); ++ fprintf(stderr, "v->blk = %s\n", v->blk); ++ fprintf(stderr, "v->drv->name = %s\n", v->drv->name); ++ fprintf(stderr, "v->priv->fd = %d\n", p->fd); ++ fprintf(stderr, "v->priv->idx = %d\n", p->idx); ++ fprintf(stderr, "v->priv->chr = %s\n", p->chr); ++ ++ return 0; ++} ++ ++static int emmc_partition_identify(struct volume *v) ++{ ++ struct emmc_priv *p = (struct emmc_priv*) v->priv; ++ __u32 deadc0de; ++ __u16 ext4; ++ size_t sz; ++ ++ fprintf(stderr, "%s(%s)\n", __FUNCTION__, v->name); ++ if (emmc_partition_load(v)) { ++ fprintf(stderr, "%s, emmc_partition_load %s failed\n", __FUNCTION__, v->name); ++ return -1; ++ } ++ ++ sz = read(p->fd, &deadc0de, sizeof(deadc0de)); ++ if (sz != sizeof(deadc0de)) { ++ fprintf(stderr, "%s, reading %s failed: %s\n", __FUNCTION__, v->name, strerror(errno)); ++ return -1; ++ } ++ ++ deadc0de = __be32_to_cpu(deadc0de); ++ if (deadc0de == 0xdeadc0de) { ++ fprintf(stderr, "ext4 is not ready - marker found\n"); ++ return FS_DEADCODE; ++ } ++ ++ /* 0x400 -> super block offset in partition ++ 0x38 -> magic offset in superblock ++ */ ++ lseek(p->fd, 0x438, SEEK_SET); ++ sz = read(p->fd, &ext4, sizeof(ext4)); ++ if (sz != sizeof(ext4)) { ++ fprintf(stderr, "reading %s failed: %s\n", v->name, strerror(errno)); ++ return -1; ++ } ++ ++ if (ext4 == 0xEF53) { ++ fprintf(stderr, "ext4 is ready\n"); ++ return FS_EXT4; ++ } ++ ++ fprintf(stderr, "No ext4 magic was found, expected %x, got %x\n", 0xEF53, ext4); ++ ++ return FS_NONE; ++} ++ ++static int emmc_partition_erase(struct volume *v, int offset, int len) ++{ ++ fprintf(stderr, "%s(%s, %d, %d)\n", __FUNCTION__, v->name, offset, len); ++ return 0; ++} ++ ++static int emmc_partition_erase_all(struct volume *v) ++{ ++ fprintf(stderr, "%s, remove files instead of erase partitions.\n", __FUNCTION__); ++ system("rm -rf /overlay/*"); ++ return 0; ++} ++ ++static int emmc_partition_init(struct volume *v) ++{ ++ fprintf(stderr, "%s(%s)\n", __FUNCTION__, v->name); ++ return 0; ++} ++ ++static int emmc_partition_read(struct volume *v, void *buf, int offset, int length) ++{ ++ struct emmc_priv *p = (struct emmc_priv*) v->priv; ++ ++ fprintf(stderr, "%s(%s, %p, %d, %d)\n", __FUNCTION__, v->name, buf, offset, length); ++ if (emmc_partition_load(v)) ++ return -1; ++ ++ if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) { ++ fprintf(stderr, "lseek/read failed\n"); ++ return -1; ++ } ++ ++ if (read(p->fd, buf, length) == -1) { ++ fprintf(stderr, "read failed\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int emmc_partition_write(struct volume *v, void *buf, int offset, int length) ++{ ++ ++ fprintf(stderr, "%s(%s, %p, %d, %d)\n", __FUNCTION__, v->name, buf, offset, length); ++ ++#if 0 ++ struct emmc_priv *p = (struct emmc_priv*) v->priv; ++ if (emmc_partition_load(v)) ++ return -1; ++ ++ if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) { ++ fprintf(stderr, "lseek/write failed at offset %d\n", offset); ++ perror("lseek"); ++ return -1; ++ } ++ ++ if (write(p->fd, buf, length) == -1) { ++ fprintf(stderr, "write failed\n"); ++ return -1; ++ } ++#endif ++ return 0; ++} ++ ++static struct driver emmc_driver = { ++ .name = "emmc", ++ .find = emmc_partition_find, ++ .init = emmc_partition_init, ++ .erase = emmc_partition_erase, ++ .erase_all = emmc_partition_erase_all, ++ .read = emmc_partition_read, ++ .write = emmc_partition_write, ++ .identify = emmc_partition_identify, ++}; ++DRIVER(emmc_driver); +Index: fstools-2015-02-25.1/libfstools/libfstools.h +=================================================================== +--- fstools-2015-02-25.1.orig/libfstools/libfstools.h ++++ fstools-2015-02-25.1/libfstools/libfstools.h +@@ -25,6 +25,7 @@ enum { + FS_JFFS2, + FS_DEADCODE, + FS_UBIFS, ++ FS_EXT4, + }; + + extern char const *extroot_prefix; +@@ -43,6 +44,7 @@ extern char* find_mount_point(char *bloc + extern int find_filesystem(char *fs); + + extern int jffs2_switch(struct volume *v); ++extern int ext4_switch(struct volume *v); + + extern int handle_whiteout(const char *dir); + extern void foreachdir(const char *dir, int (*cb)(const char*)); +Index: fstools-2015-02-25.1/libfstools/volume.h +=================================================================== +--- fstools-2015-02-25.1.orig/libfstools/volume.h ++++ fstools-2015-02-25.1/libfstools/volume.h +@@ -47,6 +47,7 @@ enum { + NANDFLASH, + NORFLASH, + UBIVOLUME, ++ EMMC, + }; + + struct volume { +Index: fstools-2015-02-25.1/libfstools/overlay.c +=================================================================== +--- fstools-2015-02-25.1.orig/libfstools/overlay.c ++++ fstools-2015-02-25.1/libfstools/overlay.c +@@ -31,6 +31,7 @@ + #include "volume.h" + + #define SWITCH_JFFS2 "/tmp/.switch_jffs2" ++#define SWITCH_EXT4 "/tmp/.switch_ext4" + + void + foreachdir(const char *dir, int (*cb)(const char*)) +@@ -106,6 +107,59 @@ switch2jffs(struct volume *v) + return fopivot("/overlay", "/rom"); + } + ++ ++static int ++switch2ext4(struct volume *v) ++{ ++ struct stat s; ++ int ret; ++ ++ if (!stat(SWITCH_EXT4, &s)) { ++ fprintf(stderr, "ext4 switch already running\n"); ++ return -1; ++ } ++ ++ creat(SWITCH_EXT4, 0600); ++ ret = mount(v->blk, "/rom/overlay", "ext4", MS_NOATIME, NULL); ++ unlink(SWITCH_EXT4); ++ if (ret) { ++ fprintf(stderr, "failed - mount -t ext4 %s /rom/overlay: %s\n", v->blk, strerror(errno)); ++ ++ char buf[128] = {0}; ++ fprintf(stderr, "maybe ext4 is not created yet.\n"); ++ snprintf(buf, sizeof(buf), "mkfs.ext4 %s", v->blk); ++ system(buf); ++ fprintf(stderr, "%s.\n", buf); ++ ++ creat(SWITCH_EXT4, 0600); ++ ret = mount(v->blk, "/rom/overlay", "ext4", MS_NOATIME, NULL); ++ unlink(SWITCH_EXT4); ++ if (ret) { ++ fprintf(stderr, "failed again, give up - mount -t ext4 %s /rom/overlay: %s\n", v->blk, strerror(errno)); ++ return -1; ++ } ++ } ++ ++ if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) { ++ fprintf(stderr, "failed - mount -o remount,ro none: %s\n", strerror(errno)); ++ return -1; ++ } ++ ++ system("cp -a /tmp/root/* /rom/overlay"); ++ ++ if (pivot("/rom", "/mnt")) { ++ fprintf(stderr, "failed - pivot /rom /mnt: %s\n", strerror(errno)); ++ return -1; ++ } ++ ++ if (mount_move("/mnt", "/tmp/root", "")) { ++ fprintf(stderr, "failed - mount -o move /mnt /tmp/root %s\n", strerror(errno)); ++ return -1; ++ } ++ ++ return fopivot("/overlay", "/rom"); ++} ++ + int + handle_whiteout(const char *dir) + { +@@ -199,6 +253,53 @@ jffs2_switch(struct volume *v) + return ret; + } + ++int ++ext4_switch(struct volume *v) ++{ ++ char *mp; ++ int ret = -1; ++ ++ if (find_overlay_mount("overlayfs:/tmp/root")) ++ return -1; ++ ++ if (find_filesystem("overlay")) { ++ fprintf(stderr, "overlayfs not found\n"); ++ return ret; ++ } ++ ++ mp = find_mount_point(v->blk, 0); ++ if (mp) { ++ fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp); ++ return -1; ++ } ++ ++ switch (volume_identify(v)) { ++ case FS_NONE: ++ fprintf(stderr, "no ext4 marker found\n"); ++ /* fall through */ ++ ++ case FS_DEADCODE: ++ ret = switch2ext4(v); ++ if (!ret) { ++ fprintf(stderr, "doing fo cleanup\n"); ++ umount2("/tmp/root", MNT_DETACH); ++ foreachdir("/overlay/", handle_whiteout); ++ } ++ break; ++ ++ case FS_EXT4: ++ ret = overlay_mount(v, "ext4"); ++ if (ret) ++ break; ++ if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) { ++ fprintf(stderr, "switching to ext4 failed\n"); ++ ret = -1; ++ } ++ break; ++ } ++ return ret; ++} ++ + static int overlay_mount_fs(struct volume *v) + { + char *fstype; +@@ -214,12 +315,29 @@ static int overlay_mount_fs(struct volum + case FS_UBIFS: + fstype = "ubifs"; + break; ++ case FS_EXT4: ++ fstype = "ext4"; ++ break; + } + + if (mount(v->blk, "/tmp/overlay", fstype, MS_NOATIME, NULL)) { + fprintf(stderr, "failed to mount -t %s %s /tmp/overlay: %s\n", + fstype, v->blk, strerror(errno)); +- return -1; ++ ++ if (0 == strcmp(fstype, "ext4")) { ++ /* maybe ext4 is not created yet! */ ++ char buf[128] = {0}; ++ fprintf(stderr, "maybe ext4 is not created yet.\n"); ++ snprintf(buf, sizeof(buf), "mkfs.ext4 %s", v->blk); ++ system(buf); ++ fprintf(stderr, "%s.\n", buf); ++ ++ if (mount(v->blk, "/tmp/overlay", fstype, MS_NOATIME, NULL)) { ++ fprintf(stderr, "again! failed to mount -t %s %s /tmp/overlay: %s\n", ++ fstype, v->blk, strerror(errno)); ++ return -1; ++ } ++ } + } + + volume_init(v); +@@ -250,7 +368,7 @@ int mount_overlay(struct volume *v) + + fprintf(stderr, "switching to overlay\n"); + if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) { +- fprintf(stderr, "switching to jffs2 failed - fallback to ramoverlay\n"); ++ fprintf(stderr, "switching to overlay failed - fallback to ramoverlay\n"); + return ramoverlay(); + } + +Index: fstools-2015-02-25.1/CMakeLists.txt +=================================================================== +--- fstools-2015-02-25.1.orig/CMakeLists.txt ++++ fstools-2015-02-25.1/CMakeLists.txt +@@ -10,6 +10,7 @@ ADD_LIBRARY(fstools SHARED + libfstools/extroot.c + libfstools/overlay.c + libfstools/volume.c ++ libfstools/emmc.c + libfstools/mtd.c + libfstools/mount.c + libfstools/ubi.c +Index: fstools-2015-02-25.1/libfstools/mtd.c +=================================================================== +--- fstools-2015-02-25.1.orig/libfstools/mtd.c ++++ fstools-2015-02-25.1/libfstools/mtd.c +@@ -153,10 +153,25 @@ static int mtd_volume_find(struct volume + char *idx = mtd_find_index(name); + struct mtd_priv *p; + char buffer[32]; ++ size_t sz; ++ FILE * fp = NULL; + + if (!idx) + return -1; + ++ /* if the mtd is created by block2mtd */ ++ fp = fopen("/proc/cmdline", "rb"); ++ if (fp) { ++ char buf[2048] = {0}; ++ sz = fread(buf, 1, sizeof(buf), fp); ++ fclose(fp); ++ buf[sizeof(buf)-1] = 0; ++ if (strstr(buf, "block2mtd")) { ++ fprintf(stderr, "%s, %s is created by block2mtd.\n", __FUNCTION__, name); ++ return -1; ++ } ++ } ++ + p = calloc(1, sizeof(struct mtd_priv)); + if (!p) + return -1; +@@ -186,12 +201,26 @@ static int mtd_volume_identify(struct vo + __u32 deadc0de; + __u16 jffs2; + size_t sz; ++ FILE * fp = NULL; + + if (mtd_volume_load(v)) { + fprintf(stderr, "reading %s failed\n", v->name); + return -1; + } + ++ /* if the mtd is created by block2mtd */ ++ fp = fopen("/proc/cmdline", "rb"); ++ if (fp) { ++ char buf[2048] = {0}; ++ sz = fread(buf, 1, sizeof(buf), fp); ++ fclose(fp); ++ buf[sizeof(buf)-1] = 0; ++ if (strstr(buf, "block2mtd")) { ++ fprintf(stderr, "%s, %s is created by block2mtd, default use FS_EXT4.\n", __FUNCTION__, v->name); ++ return FS_EXT4; ++ } ++ } ++ + sz = read(p->fd, &deadc0de, sizeof(deadc0de)); + + if (sz != sizeof(deadc0de)) { diff --git a/package/system/fstools/patches/005-mount-options.patch b/package/system/fstools/patches/005-mount-options.patch new file mode 100644 index 0000000..19a640c --- /dev/null +++ b/package/system/fstools/patches/005-mount-options.patch @@ -0,0 +1,102 @@ +Index: fstools-2015-02-25.1/block.c +=================================================================== +--- fstools-2015-02-25.1.orig/block.c ++++ fstools-2015-02-25.1/block.c +@@ -541,7 +541,7 @@ static int _cache_load(const char *path) + static void cache_load(int mtd) + { + if (mtd) { +- _cache_load("/dev/mtdblock*"); ++ //_cache_load("/dev/mtdblock*"); + _cache_load("/dev/ubiblock*"); + _cache_load("/dev/ubi?*_?*"); + } +@@ -749,6 +749,11 @@ static int mount_device(struct blkid_str + if (m && m->extroot) + return -1; + ++ char _data[128] = {0}; ++ if (strstr(pr->id->name, "fat") || strstr(pr->id->name, "ntfs")) { ++ snprintf(_data, sizeof(_data), "%s", "iocharset=utf8,uid=65534,gid=65534"); ++ } ++ + if (m) { + char *target = m->target; + char _target[32]; +@@ -760,13 +765,15 @@ static int mount_device(struct blkid_str + target = _target; + } + mkdir_p(target); ++ chmod(target, 0777); ++ fprintf(stderr, "%s\n", target); + + if (check_fs) + check_filesystem(pr); + + while (1) { + err = mount(pr->dev, target, pr->id->name, mflags, +- (m->options) ? (m->options) : ("")); ++ (m->options) ? (m->options) : _data); + if (err) { + if (!(mflags & MS_RDONLY)) + { +@@ -781,6 +788,7 @@ static int mount_device(struct blkid_str + } + } + else { ++ chmod(target, 0777); + handle_swapfiles(true); + break; + } +@@ -795,12 +803,13 @@ static int mount_device(struct blkid_str + + snprintf(target, sizeof(target), "/mnt/%s", device); + mkdir_p(target); ++ chmod(target, 0777); + + if (check_fs) + check_filesystem(pr); + + while (1) { +- err = mount(pr->dev, target, pr->id->name, mflags, ""); ++ err = mount(pr->dev, target, pr->id->name, mflags, _data); + if (err) { + if (!(mflags & MS_RDONLY)) { + ERROR("Fall back on RO mount. %s.\n", strerror(errno)); +@@ -814,6 +823,7 @@ static int mount_device(struct blkid_str + } + } + else { ++ chmod(target, 0777); + handle_swapfiles(true); + break; + } +@@ -1114,6 +1124,7 @@ static int mount_extroot(char *cfg) + struct blkid_struct_probe *pr; + struct mount *m; + int err = -1; ++ char _data[128]; + + /* Load @cfg/etc/config/fstab */ + if (config_load(cfg)) +@@ -1141,6 +1152,11 @@ static int mount_extroot(char *cfg) + pr = find_block_info(m->uuid, m->label, m->device); + } + if (pr) { ++ ++ if (strstr(pr->id->name, "fat") || strstr(pr->id->name, "ntfs")) { ++ snprintf(_data, sizeof(_data), "%s", "iocharset=utf8,uid=65534,gid=65534"); ++ } ++ + if (strncmp(pr->id->name, "ext", 3) && + strncmp(pr->id->name, "ubifs", 5)) { + ERROR("extroot: unsupported filesystem %s, try ext4\n", pr->id->name); +@@ -1159,7 +1175,7 @@ static int mount_extroot(char *cfg) + if (check_fs) + check_filesystem(pr); + +- err = mount(pr->dev, path, pr->id->name, 0, (m->options) ? (m->options) : ("")); ++ err = mount(pr->dev, path, pr->id->name, 0, (m->options) ? (m->options) : _data); + + if (err) { + ERROR("extroot: mounting %s (%s) on %s failed: %d (%s)\n", diff --git a/package/system/fstools/patches/006-7623-nand.patch b/package/system/fstools/patches/006-7623-nand.patch new file mode 100644 index 0000000..c99fc46 --- /dev/null +++ b/package/system/fstools/patches/006-7623-nand.patch @@ -0,0 +1,819 @@ +Index: fstools-2015-02-25.1/mount_root2.c +=================================================================== +--- /dev/null ++++ fstools-2015-02-25.1/mount_root2.c +@@ -0,0 +1,799 @@ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define OK (0) ++#define NG (-1) ++ ++#if 0 ++#define API(...) fprintf(stderr, " %s\n", __FUNCTION__) ++ ++#define DEBUG(...) \ ++ do {\ ++ fprintf(stderr, " ");\ ++ fprintf(stderr, __VA_ARGS__);\ ++ fprintf(stderr, " %s, L%d.\n", __FUNCTION__, __LINE__);\ ++ } while(0) ++ ++ ++#define DUMP_MOUNT() \ ++ do { \ ++ fprintf(stderr, "<<<<<<<<<< %s, L%d.\n", __FUNCTION__, __LINE__);\ ++ FILE *fp = fopen("/proc/mounts", "r");\ ++ static char line[256];\ ++ if(!fp) break;\ ++ while (fgets(line, sizeof(line), fp)) {\ ++ fprintf(stderr, "%s", line);\ ++ }\ ++ fclose(fp);\ ++ fprintf(stderr, ">>>>>>>>>> %s, L%d.\n", __FUNCTION__, __LINE__);\ ++ } while(0) ++ ++#define LIST_UBIDEV() do{\ ++ fprintf(stderr, "<%s> L%d.", __FUNCTION__, __LINE__);\ ++ system("ls -l /dev/ubi*");\ ++ system("find /sys/ -name \"*ubi*\"");\ ++ }while(0) ++#else ++#define API(...) ++#define DEBUG(...) ++#define DUMP_MOUNT() ++#define LIST_UBIDEV() ++#endif ++ ++ ++#define WAIT_UBIDEV(timeout) do{\ ++ int t = timeout;\ ++ while (t>=0 && access("/dev/ubi0_0", R_OK)) {\ ++ fprintf(stderr, "wait for /dev/ubi0_0, %d\n", t);\ ++ sleep(1); \ ++ t--;\ ++ }\ ++ if (0 == access("/dev/ubi0_0", R_OK)) \ ++ fprintf(stderr, "found /dev/ubi0_0!\n"); \ ++ else\ ++ fprintf(stderr, "timeout, break!\n");\ ++ } while(0) ++ ++ ++enum ++{ ++ FS_NONE, /* freash start */ ++ FS_DEADCODE, /* factory reset */ ++ FS_JFFS2, ++ FS_UBIFS, ++ FS_EXT4, ++}; ++ ++extern int pivot_root(const char *new_root, const char *put_old); ++ ++static char* find_mtd_dev(char *name) ++{ ++ FILE *fp = fopen("/proc/mtd", "r"); ++ static char line[256] = {'/','d','e','v','/',}; ++ ++ if(!fp) return NULL; ++ ++ while (fgets(line+5, sizeof(line)-5, fp)) ++ { ++ char *ret; ++ if ((ret = strstr(line, name)) && (ret[strlen(name)] == '"')) ++ { ++ char *eol = strstr(line, ":"); ++ if (!eol) continue; ++ *eol = '\0'; ++ break; ++ } ++ } ++ ++ fclose(fp); ++ ++ return line; ++} ++ ++ ++static int find_filesystem(char *fs) ++{ ++ FILE *fp = fopen("/proc/filesystems", "r"); ++ static char line[256]; ++ int ret = NG; ++ ++ if (!fp) ++ { ++ DEBUG("opening /proc/filesystems failed: %s", strerror(errno)); ++ goto out; ++ } ++ ++ while (ret && fgets(line, sizeof(line), fp)) ++ if (strstr(line, fs)) ++ ret = OK; ++ ++ fclose(fp); ++ ++out: ++ return ret; ++} ++ ++static int find_overlay_mount(char *overlay) ++{ ++ FILE *fp = fopen("/proc/mounts", "r"); ++ static char line[256]; ++ int ret = NG; ++ ++ if(!fp) ++ return ret; ++ ++ while (ret && fgets(line, sizeof(line), fp)) ++ if (!strncmp(line, overlay, strlen(overlay))) ++ ret = OK; ++ ++ fclose(fp); ++ ++ return ret; ++} ++ ++static char * find_mount_point(char *block, int mtd_only) ++{ ++ FILE *fp = fopen("/proc/mounts", "r"); ++ static char line[256]; ++ int len = strlen(block); ++ char *point = NULL; ++ ++ if(!fp) ++ return NULL; ++ ++ while (fgets(line, sizeof(line), fp)) ++ { ++ if (!strncmp(line, block, len)) ++ { ++ char *p = &line[len + 1]; ++ char *t = strstr(p, " "); ++ ++ if (!t) ++ { ++ fclose(fp); ++ return NULL; ++ } ++ ++ *t = '\0'; ++ t++; ++ ++ if (mtd_only && ++ strncmp(t, "ext4", 4) && ++ strncmp(t, "jffs2", 5) && ++ strncmp(t, "ubifs", 5)) ++ { ++ fclose(fp); ++ DEBUG("block is mounted with wrong fs %s", t); ++ return NULL; ++ } ++ point = p; ++ ++ break; ++ } ++ } ++ ++ fclose(fp); ++ ++ return point; ++} ++ ++static void foreachdir(const char *dir, int (*cb)(const char*)) ++{ ++ char globdir[256]; ++ glob_t gl; ++ int j; ++ ++ if (dir[strlen(dir) - 1] == '/') ++ snprintf(globdir, 256, "%s*", dir); ++ else ++ snprintf(globdir, 256, "%s/*", dir); ++ ++ if (!glob(globdir, GLOB_NOESCAPE | GLOB_MARK | GLOB_ONLYDIR, NULL, &gl)) ++ for (j = 0; j < gl.gl_pathc; j++) ++ foreachdir(gl.gl_pathv[j], cb); ++ ++ cb(dir); ++} ++ ++static int handle_whiteout(const char *dir) ++{ ++ struct stat s; ++ char link[256]; ++ ssize_t sz; ++ struct dirent **namelist; ++ int n; ++ ++ n = scandir(dir, &namelist, NULL, NULL); ++ ++ if (n < 1) ++ return NG; ++ ++ while (n--) ++ { ++ char file[256]; ++ ++ snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name); ++ if (!lstat(file, &s) && S_ISLNK(s.st_mode)) ++ { ++ sz = readlink(file, link, sizeof(link) - 1); ++ if (sz > 0) ++ { ++ char *orig; ++ ++ link[sz] = '\0'; ++ orig = strstr(&file[1], "/"); ++ if (orig && !strcmp(link, "(overlay-whiteout)")) ++ unlink(orig); ++ } ++ } ++ free(namelist[n]); ++ } ++ free(namelist); ++ ++ return OK; ++} ++ ++static int identify(char * dev) ++{ ++ unsigned magic32; ++ unsigned short magic16; ++ size_t sz; ++ int fd = -1; ++ int fstype = NG; /* fs type start from 0, NG = -1. */ ++ ++ API(); ++ ++ fd = open(dev, O_RDWR | O_SYNC); ++ if (fd < 0) ++ { ++ DEBUG("open %s failed: %s", dev, strerror(errno)); ++ goto __quit; ++ } ++ sz = read(fd, &magic32, sizeof(magic32)); ++ if (sz != sizeof(magic32)) ++ { ++ DEBUG("reading %s failed: %s", dev, strerror(errno)); ++ goto __quit; ++ } ++ ++ DEBUG("0x%X 0x%X 0x%X 0x%X 0x%X", ++ magic32, ++ __be32_to_cpu(magic32), ++ __be32_to_cpu(magic32) >> 16, ++ __be32_to_cpu(magic32) >> 8, ++ magic32 >> 8); ++ ++ magic32 = __be32_to_cpu(magic32); ++ if (magic32 == 0xDEADC0DE) ++ { ++ DEBUG("fs is not ready - deadcode found"); ++ fstype = FS_DEADCODE; ++ goto __quit; ++ } ++ ++ ++ if ((magic32 >> 8) == 0x554249) ++ { ++ DEBUG("ubi volume found."); ++ fstype = FS_UBIFS; ++ goto __quit; ++ } ++ ++ if (__be16_to_cpu(magic32 >> 16) == 0x1985) ++ { ++ DEBUG("jffs2 is ready."); ++ fstype = FS_JFFS2; ++ goto __quit; ++ } ++ ++ /* 0x400 -> super block offset in partition ++ 0x38 -> magic offset in superblock ++ */ ++ lseek(fd, 0x438, SEEK_SET); ++ sz = read(fd, &magic16, sizeof(magic16)); ++ if (sz != sizeof(magic16)) { ++ DEBUG("reading %s failed: %s", dev, strerror(errno)); ++ goto __quit; ++ } ++ ++ DEBUG("0x%X 0x%X 0x%X 0x%X", ++ magic16, ++ __be16_to_cpu(magic16), ++ __cpu_to_be16(magic16), ++ magic16 >> 8); ++ ++ if (magic16 == 0xEF53) { ++ DEBUG("ext4 is ready."); ++ fstype = FS_EXT4; ++ goto __quit; ++ } ++ ++ DEBUG("unable to find a supported magic number."); ++ fstype = FS_NONE; ++__quit: ++ if (fd>0) close(fd); ++ return fstype; ++} ++ ++static int make_ubifs(char * dev) ++{ ++ char buffer[1024]; ++ API(); ++ snprintf(buffer, sizeof(buffer), "ubiformat %s -y > /dev/console 2>&1", dev); ++ system(buffer); ++ DEBUG("%s, %s!", buffer, strerror(errno)); ++ ++ snprintf(buffer, sizeof(buffer), "ubiattach /dev/ubi_ctrl -p %s > /dev/console 2>&1", dev); ++ system(buffer); ++ DEBUG("%s, %s!", buffer, strerror(errno)); ++ ++ snprintf(buffer, sizeof(buffer), "ubimkvol /dev/ubi0 -s 64MiB -N rootfs_data > /dev/console 2>&1"); ++ system(buffer); ++ DEBUG("%s, %s!", buffer, strerror(errno)); ++ ++ snprintf(buffer, sizeof(buffer), "mknod /dev/ubi0 c 246 0"); ++ system(buffer); ++ DEBUG("%s, %s!", buffer, strerror(errno)); ++ ++ snprintf(buffer, sizeof(buffer), "mknod /dev/ubi0_0 c 246 1"); ++ system(buffer); ++ DEBUG("%s, %s!", buffer, strerror(errno)); ++ ++ return OK; ++} ++ ++static int make_jffs2(char * dev) ++{ ++ API(); ++ return OK; ++} ++ ++static int make_ext4(char * dev) ++{ ++ char buffer[128]; ++ char blkdev[128]; ++ API(); ++ ++ if (strstr(dev, "/dev/mtd") && !strstr(dev, "mtdblock")) ++ { ++ int idx = 6; ++ sscanf(dev, "/dev/mtd%d", &idx); ++ if (idx < 5) ++ { ++ DEBUG("It's dangerous to format /dev/mtd%d, give up!", idx); ++ return NG; ++ } ++ snprintf(blkdev, sizeof(blkdev), "/dev/mtdblock%d", idx); ++ DEBUG("ext4 works on block device, %s -> %s", dev, blkdev); ++ } ++ else ++ { ++ snprintf(blkdev, sizeof(blkdev), "%s", dev); ++ } ++ ++ snprintf(buffer, sizeof(buffer), "mkfs.ext4 %s", blkdev); ++ system(buffer); ++ DEBUG("%s, %s!", buffer, strerror(errno)); ++ return OK; ++} ++ ++ ++static int mount_move(char *oldroot, char *newroot, char *dir) ++{ ++#ifndef MS_MOVE ++#define MS_MOVE (1 << 13) ++#endif ++ struct stat s; ++ char olddir[64]; ++ char newdir[64]; ++ int ret; ++ ++ snprintf(olddir, sizeof(olddir), "%s%s", oldroot, dir); ++ snprintf(newdir, sizeof(newdir), "%s%s", newroot, dir); ++ ++ if (stat(olddir, &s) || !S_ISDIR(s.st_mode)) ++ return NG; ++ ++ if (stat(newdir, &s) || !S_ISDIR(s.st_mode)) ++ return NG; ++ ++ ret = mount(olddir, newdir, NULL, MS_NOATIME | MS_MOVE, NULL); ++ ++/* ++ if (ret) ++ DEBUG("failed %s %s: %s", olddir, newdir, strerror(errno)); ++*/ ++ return ret; ++} ++ ++static int pivot(char *new, char *old) ++{ ++ char pivotdir[64]; ++ int ret; ++ ++ if (mount_move("", new, "/proc")) ++ return NG; ++ ++ snprintf(pivotdir, sizeof(pivotdir), "%s%s", new, old); ++ ++ /* pivot_root(new,old) moves the root file system of the calling process ++ to the directory old and makes new the new root file system of the ++ calling process. */ ++ ret = pivot_root(new, pivotdir); ++ ++ if (ret < 0) ++ { ++ DEBUG("pivot_root failed %s %s: %s", new, pivotdir, strerror(errno)); ++ return NG; ++ } ++ ++ mount_move(old, "", "/dev"); ++ mount_move(old, "", "/tmp"); ++ mount_move(old, "", "/sys"); ++ mount_move(old, "", "/overlay"); ++ ++ return OK; ++} ++ ++ ++/* this API mount overlayfs(upper=rw_root, lower=ro_root) as root. */ ++static int fopivot(char *rw_root, char *ro_root) ++{ ++ char overlay[64], lowerdir[64]; ++ ++ if (find_filesystem("overlay")) ++ { ++ DEBUG("BUG: no suitable fs found"); ++ return NG; ++ } ++ ++ snprintf(overlay, sizeof(overlay), "overlayfs:%s", rw_root); ++ ++ /* ++ * First, try to mount without a workdir, for overlayfs v22 and before. ++ * If it fails, it means that we are probably using a v23 and ++ * later versions that require a workdir ++ */ ++ snprintf(lowerdir, sizeof(lowerdir), "lowerdir=/,upperdir=%s", rw_root); ++ if (mount(overlay, "/mnt", "overlayfs", MS_NOATIME, lowerdir)) ++ { ++ char upperdir[64], workdir[64], upgrade[64], upgrade_dest[64]; ++ struct stat st; ++ ++ snprintf(upperdir, sizeof(upperdir), "%s/upper", rw_root); ++ snprintf(workdir, sizeof(workdir), "%s/work", rw_root); ++ snprintf(upgrade, sizeof(upgrade), "%s/sysupgrade.tgz", rw_root); ++ snprintf(upgrade_dest, sizeof(upgrade_dest), "%s/sysupgrade.tgz", upperdir); ++ snprintf(lowerdir, sizeof(lowerdir), "lowerdir=/,upperdir=%s,workdir=%s", ++ upperdir, workdir); ++ ++ /* ++ * Overlay FS v23 and later requires both a upper and ++ * a work directory, both on the same filesystem, but ++ * not part of the same subtree. ++ * We can't really deal with these constraints without ++ * creating two new subdirectories in /overlay. ++ */ ++ mkdir(upperdir, 0755); ++ mkdir(workdir, 0755); ++ ++ if (stat(upgrade, &st) == 0) ++ rename(upgrade, upgrade_dest); ++ ++ /* Mainlined overlayfs has been renamed to "overlay", try that first */ ++ if (mount(overlay, "/mnt", "overlay", MS_NOATIME, lowerdir)) ++ { ++ if (mount(overlay, "/mnt", "overlayfs", MS_NOATIME, lowerdir)) ++ { ++ DEBUG("mount failed: %s, options %s", ++ strerror(errno), lowerdir); ++ return NG; ++ } ++ } ++ } ++ ++ return pivot("/mnt", ro_root); ++} ++ ++static int ram_overlay(void) ++{ ++ API(); ++ ++ mkdir("/tmp/root", 0755); ++ mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME, "mode=0755"); ++ ++ DUMP_MOUNT(); ++ fopivot("/tmp/root", "/rom"); ++ DUMP_MOUNT(); ++ return OK; ++} ++ ++ ++static int fs_overlay(int fstype, char * dev) ++{ ++ char * fs = NULL; ++ char * mp = NULL; ++ int ret = NG; ++ ++ API(); ++ ++ if (find_filesystem("overlay")) ++ { ++ DEBUG("overlayfs not enabled in kernel!"); ++ return NG; ++ } ++ ++ switch (fstype) ++ { ++ case FS_UBIFS: ++ WAIT_UBIDEV(10); ++ LIST_UBIDEV(); ++ fs = "ubifs"; ++ dev = "/dev/ubi0_0"; ++ break; ++ case FS_JFFS2: ++ fs = "jffs2"; ++ break; ++ case FS_EXT4: ++ fs = "ext4"; ++ if (strstr(dev, "/dev/mtd") && !strstr(dev, "mtdblock")) ++ { ++ char blkdev[128]; ++ int idx = 6; ++ sscanf(dev, "/dev/mtd%d", &idx); ++ if (idx < 5) ++ { ++ DEBUG("It's dangerous to format /dev/mtd%d, give up!", idx); ++ return NG; ++ } ++ snprintf(blkdev, sizeof(blkdev), "/dev/mtdblock%d", idx); ++ DEBUG("ext4 works on block device, %s -> %s", dev, blkdev); ++ dev = strdup(blkdev); ++ /* no need to free the strbuf, since the program exit soon. */ ++ } ++ break; ++ default: ++ DEBUG("you are not supposed to be here!"); ++ fs = "null"; ++ break; ++ } ++ ++ mp = find_mount_point(dev, 0); ++ if (mp && !strstr(mp, "/mnt")) /* block will mount ubifs to /mnt/ubixxx*/ ++ { ++ DEBUG("rootfs_data:%s is already mounted as %s\n", dev, mp); ++ return NG; ++ } ++ ++ DUMP_MOUNT(); ++ ++ if (find_overlay_mount("overlayfs:/tmp/root")) ++ { ++ DEBUG("overlayfs:/tmp/root not found! directly overlay fs."); ++ if (mkdir("/tmp/overlay", 0755)) ++ { ++ DEBUG("mkdir /tmp/overlay: %s", strerror(errno)); ++ } ++ ++ if (mount(dev, "/tmp/overlay", fs, MS_NOATIME, NULL)) ++ { ++ DEBUG("failed(1) to mount -t %s %s /tmp/overlay: %s. let's try again.", ++ fs, dev, strerror(errno)); ++ if (mount(dev, "/tmp/overlay", fs, MS_NOATIME, NULL)) ++ { ++ DEBUG("failed(2) to mount -t %s %s /tmp/overlay: %s", ++ fs, dev, strerror(errno)); ++ return NG; ++ } ++ } ++ ++ DEBUG("switching to overlay"); ++ if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) ++ { ++ return NG; ++ } ++ DUMP_MOUNT(); ++#if 0 ++ DEBUG("doing fo cleanup"); ++ umount2("/tmp/root", MNT_DETACH); ++ foreachdir("/overlay/", handle_whiteout); ++ DUMP_MOUNT(); ++#endif ++ ++ } ++ else ++ { ++ DEBUG("overlayfs:/tmp/root found! switch from ram_overlay to fs_overlay."); ++ if (mkdir("/rom/overlay", 0755)) ++ { ++ DEBUG("mkdir /rom/overlay: %s", strerror(errno)); ++ } ++ ++ ret = mount(dev, "/rom/overlay", fs, MS_NOATIME, NULL); ++ if (ret) ++ { ++ DEBUG("failed - mount -t %s %s /rom/overlay: %s", fs, dev, strerror(errno)); ++ return NG; ++ } ++ ++ if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) ++ { ++ DEBUG("failed - mount -o remount,ro none: %s", strerror(errno)); ++ return NG; ++ } ++ ++ //system("cat /tmp/root/etc/config/system > /dev/console 2>&1"); ++ //system("cat /rom/overlay/etc/config/system > /dev/console 2>&1"); ++ system("cp -a /tmp/root/* /rom/overlay"); ++ ++ if (pivot("/rom", "/mnt")) ++ { ++ DEBUG("failed - pivot /rom /mnt: %s", strerror(errno)); ++ return NG; ++ } ++ ++ if (mount_move("/mnt", "/tmp/root", "")) ++ { ++ DEBUG("failed - mount -o move /mnt /tmp/root %s", strerror(errno)); ++ return NG; ++ } ++ fopivot("/overlay", "/rom"); ++ ++ DEBUG("doing fo cleanup"); ++ umount2("/tmp/root", MNT_DETACH); ++ foreachdir("/overlay/", handle_whiteout); ++ } ++ ++ return OK; ++} ++ ++static int stage1(int argc, char *argv[1]) ++{ ++ char * dev = NULL; ++ int fstype = FS_NONE; ++ char tmp[64]; ++ ++ API(); ++ ++ if (!getenv("PREINIT")) ++ { ++ DEBUG("not PREINIT, skip stage1"); ++ return NG; ++ } ++ ++ /* check */ ++ dev = find_mtd_dev("rootfs_data"); ++ if (!dev) ++ { ++ DEBUG("cannot find rootfs_data"); ++ return NG; ++ } ++ ++ fstype = identify(dev); ++ switch(fstype) ++ { ++ case FS_UBIFS: ++ LIST_UBIDEV(); ++ snprintf(tmp, sizeof(tmp), "ubiattach /dev/ubi_ctrl -p %s", dev); ++ system(tmp); ++ DEBUG("%s, %s!", tmp, strerror(errno)); ++ ++ snprintf(tmp, sizeof(tmp), "mknod /dev/ubi0 c 246 0"); ++ system(tmp); ++ DEBUG("%s, %s!", tmp, strerror(errno)); ++ ++ snprintf(tmp, sizeof(tmp), "mknod /dev/ubi0_0 c 246 1"); ++ system(tmp); ++ DEBUG("%s, %s!", tmp, strerror(errno)); ++ LIST_UBIDEV(); ++ WAIT_UBIDEV(10); ++ /* break; */ ++ case FS_JFFS2: ++ /* break; */ ++ case FS_EXT4: ++ #if 0 ++ DEBUG("before fs_overlay, the world is like..."); ++ DUMP_MOUNT(); ++ system("ps w > /dev/console"); ++ system("ls -l / > /dev/console"); ++ system("ls -l /dev/ubi* > /dev/console"); ++ #endif ++ if (OK != fs_overlay(fstype, dev)) ++ { ++ DEBUG("fs_overlay failed, fallback on ram_overlay."); ++ return ram_overlay(); ++ } ++ break; ++ case FS_DEADCODE: ++ /* break; */ ++ case FS_NONE: ++ /* break; */ ++ default: ++ DEBUG("fstype %d, go ram_overlay.", fstype); ++ return ram_overlay(); ++ } ++ ++ return OK; ++} ++ ++ ++static int stage2(int argc, char *argv[1]) ++{ ++ char * dev = NULL; ++ int fstype = FS_NONE; ++ ++ /* check */ ++ dev = find_mtd_dev("rootfs_data"); ++ if (!dev) ++ { ++ DEBUG("cannot find rootfs_data"); ++ return NG; ++ } ++ ++ fstype = identify(dev); ++ switch(fstype) ++ { ++ case FS_UBIFS: ++ case FS_JFFS2: ++ case FS_EXT4: ++ DEBUG("job should have be done in stage1."); ++ return fs_overlay(fstype, dev); ++ case FS_DEADCODE: ++ /* break; */ ++ case FS_NONE: ++ /* break; */ ++ default: ++ DEBUG("no filesystem detected. create it now."); ++ if (0 == access("/proc/mt7623", R_OK)) ++ { ++ if (0 == access("/proc/emmc", R_OK)) ++ make_ext4(dev); ++ else ++ make_ubifs(dev); ++ } ++ else ++ { ++ make_jffs2(dev); ++ } ++ break; ++ } ++ ++ fstype = identify(dev); ++ if (OK != fs_overlay(fstype, dev)) ++ { ++ DEBUG("fs_overlay failed, fallback on ram_overlay."); ++ return ram_overlay(); ++ } ++ ++ return OK; ++} ++ ++ ++int main(int argc, char ** argv) ++{ ++ DEBUG("mount_root2."); ++ ++ if (argc < 2) ++ { ++ return stage1(argc, argv); ++ } ++ ++ if (!strcmp(argv[1], "done")) ++ return stage2(argc, argv); ++ return NG; ++} ++ ++ ++ +Index: fstools-2015-02-25.1/CMakeLists.txt +=================================================================== +--- fstools-2015-02-25.1.orig/CMakeLists.txt ++++ fstools-2015-02-25.1/CMakeLists.txt +@@ -52,6 +52,10 @@ ADD_EXECUTABLE(mount_root mount_root.c) + TARGET_LINK_LIBRARIES(mount_root fstools) + INSTALL(TARGETS mount_root RUNTIME DESTINATION sbin) + ++ADD_EXECUTABLE(mount_root2 mount_root2.c) ++TARGET_LINK_LIBRARIES(mount_root2 fstools) ++INSTALL(TARGETS mount_root2 RUNTIME DESTINATION sbin) ++ + ADD_EXECUTABLE(block block.c) + IF(DEFINED CMAKE_UBIFS_EXTROOT) + ADD_DEFINITIONS(-DUBIFS_EXTROOT) diff --git a/package/system/mtd/src/mtd.c b/package/system/mtd/src/mtd.c index 6dd6eea..260e17f 100644 --- a/package/system/mtd/src/mtd.c +++ b/package/system/mtd/src/mtd.c @@ -678,8 +678,27 @@ int main (int argc, char **argv) no_erase = 1; break; case 'j': + { + FILE * fp = NULL; + char * buf = NULL; + long len = 2048; // assume 2048 + size_t l = 0; + if ((fp = fopen("/proc/cpuinfo", "r"))) { + buf = (char *)malloc(len); + if (!buf) break; + memset(buf, 0, len); + l = fread(buf, 1, len, fp); + buf[len-1] = 0; + if (strstr(buf, "7623")) { + fprintf(stderr, "<%s>, yes 7623\n", __FUNCTION__); + break; + } + } + + fprintf(stderr, "<%s>, no 7623\n", __FUNCTION__); jffs2file = optarg; break; + } case 's': errno = 0; jffs2_skip_bytes = strtoul(optarg, 0, 0); diff --git a/package/system/mtd/src/trx.c b/package/system/mtd/src/trx.c index 65c2440..089d1ac 100644 --- a/package/system/mtd/src/trx.c +++ b/package/system/mtd/src/trx.c @@ -101,13 +101,14 @@ err: return -1; } +#ifndef target_ar71xx int trx_check(int imagefd, const char *mtd, char *buf, int *len) { const struct trx_header *trx = (const struct trx_header *) buf; int fd; - if (strcmp(mtd, "linux") != 0) + if (strcmp(mtd, "firmware") != 0) return 1; *len = read(imagefd, buf, 32); @@ -141,6 +142,7 @@ trx_check(int imagefd, const char *mtd, char *buf, int *len) close(fd); return 1; } +#endif int mtd_fixtrx(const char *mtd, size_t offset) diff --git a/package/system/opkg/Makefile b/package/system/opkg/Makefile index 8a63fb4..6dd3f31 100644 --- a/package/system/opkg/Makefile +++ b/package/system/opkg/Makefile @@ -113,7 +113,7 @@ define Package/opkg/Default/install done ifneq ($(CONFIG_PER_FEED_REPO_ADD_DISABLED),) for d in $(FEEDS_DISABLED); do \ - echo "# src/gz %n_$$$$d %U/$$$$d" >> $(1)/etc/opkg.conf; \ + echo "$(if $(CONFIG_PER_FEED_REPO_ADD_COMMENTED),# )src/gz %n_$$$$d %U/$$$$d" >> $(1)/etc/opkg.conf; \ done endif endif diff --git a/package/system/procd/Makefile b/package/system/procd/Makefile index 840778a..de73784 100644 --- a/package/system/procd/Makefile +++ b/package/system/procd/Makefile @@ -8,14 +8,14 @@ include $(TOPDIR)/rules.mk PKG_NAME:=procd -PKG_VERSION:=2014-07-30 +PKG_VERSION:=2014-09-15 PKG_RELEASE=$(PKG_SOURCE_VERSION) PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=git://nbd.name/luci2/procd.git PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) -PKG_SOURCE_VERSION:=35e01a9601292b2f609e65c2ddb3990cba8f378e +PKG_SOURCE_VERSION:=c1a558f7d0c1e6c1ffa5a47d557a7b45205eef1d PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz CMAKE_INSTALL:=1 diff --git a/package/system/procd/files/nand.sh b/package/system/procd/files/nand.sh index 4aa56e3..fd2f943 100644 --- a/package/system/procd/files/nand.sh +++ b/package/system/procd/files/nand.sh @@ -10,6 +10,18 @@ CI_KERNPART="kernel" # 'ubi' partition on NAND contains UBI CI_UBIPART="ubi" +ubi_mknod() { + local dir="$1" + local dev="/dev/$(basename $dir)" + + [ -e "$dev" ] && return 0 + + local devid="$(cat $dir/dev)" + local major="${devid%%:*}" + local minor="${devid##*:}" + mknod "$dev" c $major $minor +} + nand_find_volume() { local ubidevdir ubivoldir ubidevdir="/sys/devices/virtual/ubi/$1" @@ -18,6 +30,7 @@ nand_find_volume() { [ ! -d "$ubivoldir" ] && continue if [ "$( cat $ubivoldir/name )" = "$2" ]; then basename $ubivoldir + ubi_mknod "$ubivoldir" return 0 fi done @@ -33,6 +46,7 @@ nand_find_ubi() { [ ! "$mtdnum" ] && continue if [ "$mtdnum" = "$cmtdnum" ]; then ubidev=$( basename $ubidevdir ) + ubi_mknod "$ubidevdir" echo $ubidev return 0 fi @@ -122,7 +136,7 @@ nand_upgrade_prepare_ubi() { ubiattach -m "$mtdnum" sync ubidev="$( nand_find_ubi "$CI_UBIPART" )" - [ -z "$has_env" ] || { + [ "$has_env" -gt 0 ] && { ubimkvol /dev/$ubidev -n 0 -N ubootenv -s 1MiB ubimkvol /dev/$ubidev -n 1 -N ubootenv2 -s 1MiB } diff --git a/package/system/procd/files/procd.sh b/package/system/procd/files/procd.sh index 1b19ba9..6d4cd9e 100644 --- a/package/system/procd/files/procd.sh +++ b/package/system/procd/files/procd.sh @@ -75,7 +75,7 @@ _procd_close_service() { } _procd_add_array_data() { - while [ -n "$1" ]; do + while [ "$#" -gt 0 ]; do json_add_string "" "$1" shift done diff --git a/package/system/procd/patches/000-skip-watchdog.patch b/package/system/procd/patches/000-skip-watchdog.patch new file mode 100644 index 0000000..88581b5 --- /dev/null +++ b/package/system/procd/patches/000-skip-watchdog.patch @@ -0,0 +1,14 @@ +Index: procd-2014-09-15/watchdog.c +=================================================================== +--- procd-2014-09-15.orig/watchdog.c ++++ procd-2014-09-15/watchdog.c +@@ -97,7 +97,8 @@ void watchdog_init(int preinit) + + if (wdt_fd >= 0) + return; +- ++ //skip watch_dog ++ return; + wdt_timeout.cb = watchdog_timeout_cb; + if (env) { + DEBUG(2, "Watchdog handover: fd=%s\n", env); diff --git a/package/system/ubox/Makefile b/package/system/ubox/Makefile index 194789c..2d7ed29 100644 --- a/package/system/ubox/Makefile +++ b/package/system/ubox/Makefile @@ -1,13 +1,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ubox -PKG_VERSION:=2014-05-30 +PKG_VERSION:=2015-02-25.1 PKG_RELEASE=$(PKG_SOURCE_VERSION) PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=git://nbd.name/luci2/ubox.git PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) -PKG_SOURCE_VERSION:=c3d4118eee505f41c4d20a87f326479530837569 +PKG_SOURCE_VERSION:=31f0ff358b360ee461d845c1b3b5e5d38fa27925 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz CMAKE_INSTALL:=1 PKG_CHECK_FORMAT_SECURITY:=0 diff --git a/package/system/ubox/files/log.init b/package/system/ubox/files/log.init index be38297..3e06fa5 100644 --- a/package/system/ubox/files/log.init +++ b/package/system/ubox/files/log.init @@ -19,7 +19,6 @@ validate_log_section() 'log_port:port:514' \ 'log_proto:or("tcp", "udp"):udp' \ 'log_prefix:string' - return $? } validate_log_daemon() @@ -27,7 +26,6 @@ validate_log_daemon() uci_validate_section system system "${1}" \ 'log_size:uinteger:0' \ 'log_buffer_size:uinteger:0' - return $? } start_service_daemon() diff --git a/package/system/ubox/patches/001-insmod.patch b/package/system/ubox/patches/001-insmod.patch new file mode 100644 index 0000000..af858d9 --- /dev/null +++ b/package/system/ubox/patches/001-insmod.patch @@ -0,0 +1,47 @@ +Index: ubox-2015-02-25.1/kmodloader.c +=================================================================== +--- ubox-2015-02-25.1.orig/kmodloader.c ++++ ubox-2015-02-25.1/kmodloader.c +@@ -483,14 +483,15 @@ static int insert_module(char *path, con + return ret; + } + +- data = malloc(s.st_size); +- if (read(fd, data, s.st_size) == s.st_size) +- ret = syscall(__NR_init_module, data, (unsigned long) s.st_size, options); +- else ++ data = mmap(NULL, s.st_size, PROT_READ, ++ MAP_PRIVATE, fd, 0); ++ if (data == MAP_FAILED) + ERROR("failed to read full module %s\n", path); ++ else ++ ret = syscall(__NR_init_module, data, (unsigned long) s.st_size, options); + ++ munmap(data, s.st_size); + close(fd); +- free(data); + + return ret; + } +@@ -574,15 +575,18 @@ static int main_insmod(int argc, char ** + if (argc < 2) + return print_insmod_usage(); + ++ if (init_module_folders()) ++ return -1; ++ ++ if (scan_loaded_modules()) ++ return -1; ++ + name = get_module_name(argv[1]); + if (!name) { + ERROR("cannot find module - %s\n", argv[1]); + return -1; + } + +- if (scan_loaded_modules()) +- return -1; +- + if (find_module(name)) { + ERROR("module is already loaded - %s\n", name); + return -1; diff --git a/package/system/ubus/Makefile b/package/system/ubus/Makefile index 7370f1b..194b3e3 100644 --- a/package/system/ubus/Makefile +++ b/package/system/ubus/Makefile @@ -1,13 +1,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ubus -PKG_VERSION:=2014-07-03 +PKG_VERSION:=2014-09-17 PKG_RELEASE=$(PKG_SOURCE_VERSION) PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=git://nbd.name/luci2/ubus.git PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) -PKG_SOURCE_VERSION:=f688c7ad0b2435a89bfd13f5496cabf596b54c8f +PKG_SOURCE_VERSION:=4c4f35cf2230d70b9ddd87638ca911e8a563f2f3 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz PKG_MIRROR_MD5SUM:= CMAKE_INSTALL:=1 -- cgit v1.1