参考:
http://blog.sina.com.cn/s/blog_b91bb3470101opv9.html build-cacheimage-target函数解释索引。
http://www.cnblogs.com/gansc23/archive/2011/04/22/2024861.html 伪目标索引。
http://blog.sina.com.cn/s/blog_b91bb3470101opv9.html Android源代码 如何生成system.img文件
http://blog.csdn.net/luoshengyang/article/details/37749383 generate-userimage-prop-dictionary函数和build_image文件的一些解释
http://blog.csdn.net/luoshengyang/article/details/20501657
http://www.cnblogs.com/mr-raptor/archive/2012/06/08/2541571.html 一些变量的含义用法
http://blog.csdn.net/luoshengyang/article/details/37613135 file_contexts文件的作用意义, 主讲Android安全机制框架分析 值得学习
假设要添加flashdata分区,
以海斯3798芯片emmc为例:一、综述
1,首先要在系统中加入真实的flashdata分区,
在文件Hi3798MV100-emmc.xml配置文件中加入如下红色字体内容
<Part Sel="0" PartitionName="wifi" FlashType="emmc" FileSystem="ext3/4" Start="6906M" Length="30M" SelectFile=""/>
<Part Sel="0" PartitionName="flashdata" FlashType="emmc" FileSystem="ext3/4" Start="6936M" Length="40M" SelectFile=""/>
<Part Sel="0" PartitionName="other" FlashType="emmc" FileSystem="ext3/4" Start="6976M" Length="-" SelectFile=""/>
这样在烧写的时候,就会烧写出flashdata分区。
2,接下来需要做一个flashdata.img(名字这东西都是随便起的只是为了对应方便日后自己知道这个文件是干嘛的,所以和上面起了相同的名字)的文件,再把这个img文件转化为ext4类型的文件。即完成烧写文件的制作。完成以后,再烧写的时候放到上一步骤中加入的flashdata分区即可。这样我们打包的文件就烧写进入了flashdata设备内。
3,在系统根目录/下面创建一个flashdata文件,然后挂在到flashdata分区上。这样就大功告成。
二、制作过程
1,制作flashdata.img文件
img文件的制作,我们是仿照cache.img制作的。
下面我们拿其仿照的cache做分析:
# -----------------------------------------------------------------
# cache partition image
ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE 如果定义了BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE就做下面的操作,这个条件可以不要。这个变量的定义在SDK\device\hisilicon\Hi3798MV100\BoardConfig.mk文件中定义,描述的是cache的类型为ext4。
INTERNAL_CACHEIMAGE_FILES := \ 从提取cache相关文件,$(filter %.o,$(files))表示调用Makefile的filter函数,过滤“$filter”集,只要其中 模式为“%.o”的内容。%表示所有
$(filter $(TARGET_OUT_CACHE)/%,$(ALL_DEFAULT_INSTALLED_MODULES)) 变量TARGET_OUT_CACHE在/build/core/envsetup.mk:217:TARGET_OUT_CACHE := $(PRODUCT_OUT)/cache。android在编译时会将所有要安装的模块都保存在变量ALL_DEFAULT_INSTALLED_MODULES中,并且将build/core/Makefie文件加载进来。
cacheimage_intermediates := \ 拼接一个cache的文件夹路径,供之后使用。函数intermediates-dir-for的作用是拼接一个intermediates文件路径,其作用后面会讲。
$(call intermediates-dir-for,PACKAGING,cache) # call是Makefile的一个用来创建新的参数化的函数, 其语法是:$(call <expression>;,<parm1>;,<parm2>;,<parm3>;...) 。 <expression>的参数中的变量,会被参数<parm1>;,<parm2>;,<parm3>;依次取代。
BUILT_CACHEIMAGE_TARGET := $(PRODUCT_OUT)/cache.img
变量BUILT_CACHEIMAGE_TARGET定义了cache.img的输出路径值。PRODUCT_OUT变量在./build/core/envsetup.mk中定义,表示到out\target\product\Hi3798MV100\目录
define build-cacheimage-target 定义函数build-cacheimage-target供调用
$(call pretty,"Target cache fs image: $(INSTALLED_CACHEIMAGE_TARGET)") pretty函数是一个打印函数
@mkdir -p $(TARGET_OUT_CACHE) 表示在创建cache输出路径,-p选项允许你一次性创建多层次的目录,而不是一次只创建单独的目录
@mkdir -p $(cacheimage_intermediates) && rm -rf $(cacheimage_intermediates)/cache_image_info.txt
$(call generate-userimage-prop-dictionary, $(cacheimage_intermediates)/cache_image_info.txt, skip_fsck=true) 命令generate-userimage-prop-dictionary,用来生成一个属性文件cache_image_info.txt。描述cache以及其他模块的大小,system,userdata等img都会调用该函数。其声称文件的名称为XXX_image_info.txt。他的也是实现在build/core/Makefile文件中它的内容是通过一系列的echo命令生成的,每一行都是“key=value“形式。其中,与文件安全上下文相关的是最后一行:selinux_fc=$(SELINUX_FC) 变量SELINUX_FC指向一个file_contexts文件。这个file_contexts文件就是我们在前面一篇文章SEAndroid安全机制框架分析中提到的file_contexts文件,用来描述文件的安全上下文。我们知道,system.img镜像文件是安装在目标设备上的/system目录的,因此我们就观察一下在file_contexts文件中与/system目录相关的文件的安全上下文是如何设置的。
文件file_contexts最开始是位于SDK/build/external/sepolicy目录中的,经过编译后,就会保存在$OUT/root目录中,其中$OUT指向的是产品输出目录。打开$OUT/root/file_contexts文件,我们就可以看到与/system,data,cache等目录相关的文件的安全上下文的设置规则。这个至关重要,与我们之后能否生成img有直接关系。 Makefile foreach 函数
$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \ 参见下面Makefile foreach 函数
./build/tools/releasetools/build_image.py \
$(TARGET_OUT_CACHE) $(cacheimage_intermediates)/cache_image_info.txt $(INSTALLED_CACHEIMAGE_TARGET)
$(hide) $(call assert-max-image-size,$(INSTALLED_CACHEIMAGE_TARGET),$(BOARD_CACHEIMAGE_PARTITION_SIZE),yaffs)
endef
BOARD_CACHEIMAGE_PARTITION_SIZE定义在SDK\device\hisilicon\Hi3798MV100\BoardConfig.mk文件中定义,描述的是cache的大小。
文件build/tools/releasetools/build_image.py实现image的压缩,它的入口函数main的实现如下所示:
def main(argv):
......
in_dir = argv[0]
glob_dict_file = argv[1]
out_file = argv[2]
glob_dict = LoadGlobalDict(glob_dict_file)
image_filename = os.path.basename(out_file)
mount_point = ""
if image_filename == "system.img":
mount_point = "system"
elif image_filename == "userdata.img":
mount_point = "data"
elif image_filename == "cache.img":
mount_point = "cache"
elif image_filename == "vendor.img":
mount_point = "vendor"
else:
print >> sys.stderr, "error: unknown image file name ", image_filename
exit(1)
image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
if not BuildImage(in_dir, image_properties, out_file):
print >> sys.stderr, "error: failed to build %s from %s" % (out_file, in_dir)
exit(1)
if __name__ == '__main__':
main(sys.argv[1:])
参数argv[1]指向的就是我们上面提到的属性文件system_image_info.txt,最终保存在本地变量glob_dict_file中。另外一个参数argv[2]指向的要输出的system.img文件路径,最终保存在本地变量out_file中。
函数LoadGlobalDict用来打开属性文件system_image_info.txt,并且将它每一行的key和value提取出来,并且保在字典glob_dict中。注意,这个字典glob_dict包含有一个key等于selinux_fc、value等于file_contexts文件路径的项。
接下来再通过os.path.basename将输出的文件路径out_file的最后一项提取出来,就可以得到image_filename的值为”system.img“,因此再接下来就会得到本地变量mount_point的值为”system“,表示我们现在正在打包的是system.img文件。
函数ImagePropFromGlobalDict用来从字典glob_dict中提取与安装点mount_point相关的项,并且保存在另外一个字典中返回给调用者,它的实现如下所示:
def ImagePropFromGlobalDict(glob_dict, mount_point):
"""Build an image property dictionary from the global dictionary.
Args:
glob_dict: the global dictionary from the build system.
mount_point: such as "system", "data" etc.
"""
d = {}
def copy_prop(src_p, dest_p):
if src_p in glob_dict:
d[dest_p] = str(glob_dict[src_p])
common_props = (
"extfs_sparse_flag",
"mkyaffs2_extra_flags",
"selinux_fc",
"skip_fsck",
)
for p in common_props:
copy_prop(p, p)
d["mount_point"] = mount_point
if mount_point == "system":
copy_prop("fs_type", "fs_type")
copy_prop("system_size", "partition_size")
elif mount_point == "data":
copy_prop("fs_type", "fs_type")
copy_prop("userdata_size", "partition_size")
elif mount_point == "cache":
copy_prop("cache_fs_type", "fs_type")
copy_prop("cache_size", "partition_size")
elif mount_point == "vendor":
copy_prop("vendor_fs_type", "fs_type")
copy_prop("vendor_size", "partition_size")
return d
从这里就可以看出,函数ImagePropFromGlobalDict返回给调用者的字典包含一个以selinux_fc为key值的项,它的值指向上述分析的fiiles_contexts文件。
回到函数main中,最后它调用另外一个函数BuildImage来生成最终的system.img文件,它的实现如下所示:
def BuildImage(in_dir, prop_dict, out_file):
"""Build an image to out_file from in_dir with property prop_dict.
Args:
in_dir: path of input directory.
prop_dict: property dictionary.
out_file: path of the output image file.
Returns:
True iff the image is built successfully.
"""
build_command = []
fs_type = prop_dict.get("fs_type", "")
run_fsck = False
if fs_type.startswith("ext"):
build_command = ["mkuserimg.sh"]
......
build_command.extend([in_dir, out_file, fs_type,
prop_dict["mount_point"]])
......
if "selinux_fc" in prop_dict:
build_command.append(prop_dict["selinux_fc"])
else:
build_command = ["mkyaffs2image", "-f"]
......
build_command.append(in_dir)
build_command.append(out_file)
if "selinux_fc" in prop_dict:
build_command.append(prop_dict["selinux_fc"])
build_command.append(prop_dict["mount_point"])
exit_code = RunCommand(build_command)
......
return exit_code == 0
参数prop_dict指向的就是前面调用ImagePropFromGlobalDict获得的字典,如果它里面包含有一个key为"fs_type"的项,并且它的value等于"ext",那么就意味着将要制作ext格式的system.img镜像文件,否则的话,就意味着将要制作yaffs2格式的system.img镜像文件。前者通过命令mkuserimg来生成,而后者通过命令mkyaffs2image来生成。无论生成的是什么格式的system.img镜像文件, 只要参数prop_dict包含有一个key为'selinux_fc'的项,那么都会将它的value提取出来,并且作为一个参数传递给命令mkuserimg或者mkyaffs2image使用。
根据前面我们的分析,参数prop_dict描述的字典包含有key为'selinux_fc'的项,并且它的value描述的就是我们在上面提到的file_contexts的路径。当我们将这个file_contexts文件路径传递给命令mkuserimg或者mkyaffs2image时,后者就会根据它设置的规则给打包在system.img里面的文件关联安全上下文。这样我们就获得了一个关联有安全上下文的system.img镜像文件了。
# We just build this directly to the install location. 这句话的解释直接意思是我们直接安装他的位置,我理解的意思是此处即是我们直接拿到结果的位置
INSTALLED_CACHEIMAGE_TARGET := $(BUILT_CACHEIMAGE_TARGET) 变量INSTALLED_CACHEIMAGE_TARGET赋值为BUILT_CACHEIMAGE_TARGET,在文件/device/hisilicon/bigfish/build/bigfish.mk:5:INSTALLED_CACHEIMAGE_TARGET := $(PRODUCT_OUT)/cache.img被再次赋予同样地值,不知道为何。个人推断是在其另一个调用文件./device/hisilicon/bigfish/build/ext4.mk无法识别该变量所以再次声明?
$(INSTALLED_CACHEIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_CACHEIMAGE_FILES)
$(build-cacheimage-target)
上面的表达式表示变量INSTALLED_CACHEIMAGE_TARGET依赖于以上三个表达式。其中 INTERNAL_USERIMAGES_DEPS描述的是制作system.img镜像所依赖的工具。例如,如果要制作的system.img使用的是yaffs2文件系统,那么对应工具就是mkyaffs2image。INTERNAL_CACHEIMAGE_FILES从上面的定义中取出的值为空,也就是没有找到包含cache相关的文件。build-cacheimage-target表示的函数制作了img。
.PHONY: cacheimage-nodeps
cacheimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS)
$(build-cacheimage-target)
一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样。于是整个过程可以这样写:
.PHONY: clean
clean:
rm *.o temp
伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile中,那么你可以使用“伪目标”这个特性:
all: prog1 prog2 prog3
.PHONY : all
prog1: prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2: prog2.o
cc -o prog2 prog2.o
prog3: prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
我们知道,Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三个目标就总是不如“all”这个目标新。所以,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。“.PHONY: all”声明了“all”这个目标为“伪目标”。
endif # BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE 上面的if描述结束
以上制作过程,我们可以参考:
a, 如果最后需要打包成升级包,则在制作img的当前makefile文件中变量BUILT_TARGET_FILES_PACKAGE里面添加$(INSTALLED_CACHEIMAGE_TARGET) \
b,img输出路径变量TARGET_OUT_FLASHDATA定义在SDK\build\core\envsetup.mk文件中
TARGET_OUT_DATA := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DATA)
......
TARGET_OUT_FLASHDATA := $(PRODUCT_OUT)/flashdata
TARGET_OUT_CACHE := $(PRODUCT_OUT)/cache
c,理论上讲我们本来需要在SDK\build\core\main.mk文件中,(其实不加入也是可以的,具体原因不明。个人猜测这个是android自带的,实际可能并没有应用。此项需要自己测试是否有用,这里只做提示)
变量dont_bother_goals :=.....
......
cacheimage-nodeps \
flashdataimage-nodeps \ \伪目标
.......
.PHONY: cacheimage
cacheimage: $(INSTALLED_CACHEIMAGE_TARGET)
.PHONY: flashdataimage
flashdataimage: $(INSTALLED_FLASHDATAIMAGE_TARGET)
.......
.PHONY: droidcore
droidcore: ......
.....
$(INSTALLED_CACHEIMAGE_TARGET) \
$(INSTALLED_FLASHDATAIMAGE_TARGET) \
d,文件SDK\build\core\Makefile中
define generate-userimage-prop-dictionary
......
$(if $(BOARD_CACHEIMAGE_PARTITION_SIZE),$(hide) echo "cache_size=$(BOARD_CACHEIMAGE_PARTITION_SIZE)" >> $(1))
$(if $(BOARD_FLASHDATAIMAGE_FILE_SYSTEM_TYPE),$(hide) echo "flashdata_fs_type=$(BOARD_FLASHDATAIMAGE_FILE_SYSTEM_TYPE)" >> $(1)) #此处可以不加,有些分区没有文件所以烧机时没有初始化(如cache)才需要加
$(if $(BOARD_FLASHDATAIMAGE_PARTITION_SIZE),$(hide) echo "flashdata_size=$(BOARD_FLASHDATAIMAGE_PARTITION_SIZE)" >> $(1))
......
endif # BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
# -----------------------------------------------------------------
# flashdata partition image
ifdef BOARD_FLASHDATAIMAGE_FILE_SYSTEM_TYPE
INTERNAL_FLASHDATAIMAGE_FILES := \
$(filter $(TARGET_OUT_FLASHDATA)/%,$(ALL_DEFAULT_INSTALLED_MODULES))
flashdataimage_intermediates := \
$(call intermediates-dir-for,PACKAGING,flashdata)
BUILT_FLASHDATAIMAGE_TARGET := $(PRODUCT_OUT)/flashdata.img
define build-flashdataimage-target
$(call pretty,"Target flashdata fs image: $(INSTALLED_FLASHDATAIMAGE_TARGET)")
@mkdir -p $(TARGET_OUT_FLASHDATA)
@mkdir -p $(flashdataimage_intermediates) && rm -rf $(flashdataimage_intermediates)/flashdata_image_info.txt
$(call generate-userimage-prop-dictionary, $(flashdataimage_intermediates)/flashdata_image_info.txt, skip_fsck=true)
$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
./build/tools/releasetools/build_image.py \
$(TARGET_OUT_FLASHDATA) $(flashdataimage_intermediates)/flashdata_image_info.txt $(INSTALLED_FLASHDATAIMAGE_TARGET)
$(hide) $(call assert-max-image-size,$(INSTALLED_FLASHDATAIMAGE_TARGET),$(BOARD_FLASHDATAIMAGE_PARTITION_SIZE),yaffs)
endef
# We just build this directly to the install location.
INSTALLED_FLASHDATAIMAGE_TARGET := $(BUILT_FLASHDATAIMAGE_TARGET)
$(INSTALLED_FLASHDATAIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_FLASHDATAIMAGE_FILES)
$(build-flashdataimage-target)
.PHONY: flashdataimage-nodeps
flashdataimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS)
$(build-flashdataimage-target)
endif # BOARD_FLASHDATAIMAGE_FILE_SYSTEM_TYPE
.......
$(BUILT_TARGET_FILES_PACKAGE): \
......
$(INSTALLED_CACHEIMAGE_TARGET) \
$(INSTALLED_FLASHDATAIMAGE_TARGET) \
......
e,文件SDK\build\tools\releasetools\build_image.py中加入
if mount_point == "system":
......
copy_prop("cache_size", "partition_size")
elif mount_point == "flashdata":
copy_prop("fs_type", "fs_type") #如果分区和默认的fs_type文件类型不一样,就需要用到私有的变量flashdata_fs_type,如果一致并且在BoardConfig.mk文件中没有定义文件类型就不要在这里乱定义,否则可能编译不过(其实是百分之百编译不过,娃哈哈o(∩_∩)o 哈哈)可以定义的和data一样,为默认的 copy_prop("fs_type", "fs_type")
copy_prop("flashdata_size", "partition_size")
......
f image_filename == "system.img"
......
mount_point = "cache"
elif image_filename == "flashdata.img":
mount_point = "flashdata"
f,在文件SDKbuild\tools\releasetools\common.py中加入
makeint("cache_size")
makeint("flashdata_size")
makeint("recovery_size")
参考cache写的不知道具体意思
g,在文件SDK\device\hisilicon\Hi3798MV100\BoardConfig.mk中
.......
BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
BOARD_FLASHDATAIMAGE_PARTITION_SIZE := 524288000
#BOARD_FLASHDATAIMAGE_FILE_SYSTEM_TYPE := ext4 #如果有分区有文件可不用定义,直接在放置文件时会设置文件系统类型。建议不用定义。
BOARD_SDCARDIMAGE_PARTITION_SIZE := 2684354560
.......
定义两个变量方便在\build_050_old\core\Makefile文件中的generate-userimage-prop-dictionary函数使用,该函数获取flashdata的大小和文件类型
h,文件\device\hisilicon\bigfish\build\bigfish.mk中
......
INSTALLED_CACHEIMAGE_TARGET := $(PRODUCT_OUT)/cache.img
INSTALLED_FLASHDATAIMAGE_TARGET := $(PRODUCT_OUT)/flashdata.img
INSTALLED_SDCARDIMAGE_TARGET := $(PRODUCT_OUT)/sdcard.img
......
为变量INSTALLED_FLASHDATAIMAGE_TARGET赋值,编译之后调用。此处和SDK\build\core\Makefile两处均赋值相同,第一次是给全局变量存储以便于本次查阅调用。
i,文件\device\hisilicon\bigfish\build\ext4.mk中(如果是nand修改的是nui.mk)
.....
MAKE_EXT4FS_TOOLS := $(HOST_OUT_EXECUTABLES)/make_ext4fs
$(EXT4_IMG):$(INSTALLED_SYSTEMIMAGE) $(INSTALLED_USERDATAIMAGE_TARGET) $(INSTALLED_CACHEIMAGE_TARGET) $(INSTALLED_FLASHDATAIMAGE_TARGET)
mkdir -p $(TARGET_OUT_SDCARD)
......
此处加入的原因是,ext4fs依赖于INSTALLED_FLASHDATAIMAGE_TARGET从而在,编译ext4时调用flashdata.img的编译方法。
j, 文件/external/sepolicy/file_contexts 描述了文件。需要加入如下语句:
.......
/cache/backup(/.*)? u:object_r:cache_backup_file:s0
#############################
# Flashdata files
#
/flashdata(/.*)? u:object_r:cache_file:s0
/flashdata/.*\.data u:object_r:cache_backup_file:s0
/flashdata/.*\.restore u:object_r:cache_backup_file:s0
# LocalTransport (backup) uses this directory
/flashdata/backup(/.*)? u:object_r:cache_backup_file:s0
#############################
# sysfs files
.......
file_contexts文件的详细内容可参看下面附录“file_contexts文件”或http://blog.csdn.net/luoshengyang/article/details/37613135
参考:
http://blog.sina.com.cn/s/blog_b91bb3470101opv9.html build-cacheimage-target函数解释索引。
http://www.cnblogs.com/gansc23/archive/2011/04/22/2024861.html 伪目标索引。
http://blog.sina.com.cn/s/blog_b91bb3470101opv9.html Android源代码 如何生成system.img文件
http://blog.csdn.net/luoshengyang/article/details/37749383 generate-userimage-prop-dictionary函数和build_image文件的一些解释
http://blog.csdn.net/luoshengyang/article/details/20501657
http://www.cnblogs.com/mr-raptor/archive/2012/06/08/2541571.html 一些变量的含义用法
http://blog.csdn.net/luoshengyang/article/details/37613135 file_contexts文件的作用意义, 主讲Android安全机制框架分析 值得学习
2,由flashdata.img文件制作EXT4文件
在文件\device\hisilicon\bigfish\build\ext4.mk中(如果是nand修改的是nui.mk) 加入一句
cp -r $(PRODUCT_OUT)/cache.img $(EMMC_PRODUCT_OUT)/cache_$(HardwareVersion)_$(SoftwareVersion).ext4
cp -r $(PRODUCT_OUT)/flashdata.img $(EMMC_PRODUCT_OUT)/flashdata_$(HardwareVersion)_$(SoftwareVersion).ext4
cp -r $(PRODUCT_OUT)/sdcard.img $(EMMC_PRODUCT_OUT)/sdcard_$(HardwareVersion)_$(SoftwareVersion).ext4
即可。这样img文件便会被转化为ext4文件存储。
三、flashdata分区挂载
找到init.rc文件,目前看海斯的init.rc文件在\device\hisilicon\bigfish\etc\init.rc修改如下:
mkdir /system
mkdir /data 0771 system system
mkdir /flashdata 0771 system system
......
on emmc-fs
mount ext4 ext4@system /system ro
mount ext4 ext4@userdata /data nosuid nodev
mount ext4 ext4@flashdata /flashdata nosuid nodev
mount ext4 ext4@cache /cache nosuid nodev
on post-fs-data
# We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
chmod 0771 /data
# We restorecon /data in case the userdata partition has been reset.
restorecon /data
# We chown/chmod /flashdata again so because mount is run as root + defaults
chown system system /flashdata
chmod 0771 /flashdata
# We restorecon /flashdata in case the userdata partition has been reset.
restorecon /flashdata
.......