Luci 上传文件源码分析

前言

怕忘记, 在这里保存一下
所有源码都在system.luaaction_flashops方法下

源码备注

####声明变量部分

1
2
3
4
5
6
7
8
9
10
function action_flashops()
local sys = require "luci.sys"
local fs = require "luci.fs"

local upgrade_avail = nixio.fs.access("/lib/upgrade/platform.sh")
local reset_avail = os.execute([[grep '"rootfs_data"' /proc/mtd >/dev/null 2>&1]]) == 0

local restore_cmd = "tar -xzC/ >/dev/null 2>&1"
local backup_cmd = "sysupgrade --create-backup - 2>/dev/null"
local image_tmp = "/tmp/firmware.img"

这部分没啥好说, 主要是image_tmp 这个变量
image_tmp 是上传的文件保存的位置

####声明方法部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
local function image_supported()
-- XXX: yay...
return ( 0 == os.execute(
". /lib/functions.sh; " ..
"include /lib/upgrade; " ..
"platform_check_image %q >/dev/null"
% image_tmp
) )
end

local function image_checksum()
return (luci.sys.exec("md5sum %q" % image_tmp):match("^([^%s]+)"))
end

local function storage_size()
local size = 0
if nixio.fs.access("/proc/mtd") then
for l in io.lines("/proc/mtd") do
local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"')
if n == "linux" or n == "firmware" then
size = tonumber(s, 16)
break
end
end
elseif nixio.fs.access("/proc/partitions") then
for l in io.lines("/proc/partitions") do
local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)')
if b and n and not n:match('[0-9]') then
size = tonumber(b) * 1024
break
end
end
end
return size
end

####接收文件部分(setfilehandler)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
local fp
luci.http.setfilehandler(
function(meta, chunk, eof)
if not fp then
if meta and meta.name == "image" then
fp = io.open(image_tmp, "w")
else
fp = io.popen(restore_cmd, "w")
end
end
if chunk then
fp:write(chunk)
end
if eof then
fp:close()
end
end
)

这部分一定要放在声明下面, 逻辑的上面, 就是说luci.http.setfilehandler上面只能有声明的代码
并且它接收完毕才会只系下面的代码, 不是异步的

逻辑部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
	...

elseif luci.http.formvalue("image") or luci.http.formvalue("step") then
--
-- Initiate firmware flash
--
local step = tonumber(luci.http.formvalue("step") or 1)
if step == 1 then
if image_supported() then
luci.template.render("admin_system/upgrade", {
checksum = image_checksum(),
storage = storage_size(),
size = nixio.fs.stat(image_tmp).size,
keep = (not not luci.http.formvalue("keep"))
})
else
nixio.fs.unlink(image_tmp)
luci.template.render("admin_system/flashops", {
reset_avail = reset_avail,
upgrade_avail = upgrade_avail,
image_invalid = true
})
end
--
-- Start sysupgrade flash
--
elseif step == 2 then
local keep = (luci.http.formvalue("keep") == "1") and "" or "-n"
luci.template.render("admin_system/applyreboot", {
title = luci.i18n.translate("Flashing..."),
msg = luci.i18n.translate("The system is flashing now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a few minutes before you try to reconnect. It might be necessary to renew the address of your computer to reach the device again, depending on your settings."),
addr = (#keep > 0) and "192.168.1.1" or nil
})
fork_exec("killall dropbear uhttpd; sleep 1; /sbin/sysupgrade %s %q" %{ keep, image_tmp })
end

...

end
end

PS. 已经注释掉无关部分
接收完文件之后, 这一句luci.http.formvalue("image")就通过了, 跟住就检查文件的正确性, 正确就渲染admin_system/upgrade.htm, 错误就渲染admin_system/flashops.htm并且删除文件nixio.fs.unlink(image_tmp)
admin_system/flashops.htm没啥代码, 主要是提交step = 2回来,
所以升级代码是这句fork_exec("killall dropbear uhttpd; sleep 1; /sbin/sysupgrade %s %q" %{ keep, image_tmp })


Arylo Yeung, Typescripter, 暂留在鹅厂
tomail:arylo.open@gmail.com