new sysupgrade fixes part 2
[project/luci.git] / libs / sys / luasrc / sys / mtdow.lua
1 --[[
2 LuCI - Lua Configuration Interface
3
4 Copyright 2008 Steven Barth <steven@midlink.org>
5 Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14 ]]--
15
16 local io = require "io"
17 local os = require "os"
18 local fs = require "luci.fs"
19 local util = require "luci.util"
20 local ltn12 = require "luci.ltn12"
21 local posix = require "posix"
22
23 local type, assert, error, ipairs = type, assert, error, ipairs
24
25 module "luci.sys.mtdow"
26
27 WRITE_IMAGE = 0
28 WRITE_COMBINED = 1
29 WRITE_EMULATED = 2
30
31 ERROR_INTERNAL = 1
32 ERROR_NOTFOUND = 2
33 ERROR_RESOURCE = 3
34 ERROR_NODETECT = 4
35 ERROR_NOTAVAIL = 5
36 ERROR_NOSTREAM = 6
37 ERROR_INVMAGIC = 7
38
39 Writer = util.class()
40
41 -- x86
42 EmulatedWriter = util.class(Writer)
43 EmulatedWriter.blocks = {
44         image = {
45                 magic = "eb48",
46                 device = "/dev/hda",
47                 write = WRITE_EMULATED
48         }
49 }
50
51 function EmulatedWriter.write_block(self, name, imagestream, appendpattern)
52         if appendpattern then
53                 os.execute("grep rootfs /proc/mtd >/dev/null || "
54                         .. "{ echo /dev/hda2,65536,rootfs > "
55                         .. "/sys/module/block2mtd/parameters/block2mtd }")
56         end
57         return Writer.write_block(self, name, imagestream, appendpattern)
58 end
59
60 -- Broadcom
61 CFEWriter = util.class(Writer)
62 CFEWriter.blocks = {
63         image = {
64                 magic = {"4844", "5735"},
65                 device = "linux",
66                 write = WRITE_IMAGE,
67                 system = true
68         }
69 }
70
71 -- Magicbox
72 CommonWriter = util.class(Writer)
73 CommonWriter.blocks = {
74         image = {
75                 device = "linux",
76                 write = WRITE_COMBINED,
77                 system = true
78         }
79 }
80
81 -- Atheros
82 RedWriter = util.class(Writer)
83 RedWriter.blocks = {
84         kernel = {
85                 device = "vmlinux.bin.l7",
86                 write = WRITE_IMAGE
87         },
88         rootfs = {
89                 device = "rootfs",
90                 write = WRITE_COMBINED,
91                 system = true
92         } 
93 }
94
95 -- Auto Detect
96 function native_writer()
97         local w = Writer()
98         
99         -- Detect amd64 / x86
100         local x86 = {"x86_64", "i386", "i486", "i586", "i686"}
101         if util.contains(x86, posix.uname("%m")) then
102                 return EmulatedWriter()
103         end
104         
105         -- Detect CFE
106         if w:_find_mtdblock("cfe") and w:_find_mtdblock("linux") then
107                 return CFEWriter()
108         end
109         
110         -- Detect Redboot
111         if w:_find_mtdblock("RedBoot") and w:_find_mtdblock("vmlinux.bin.l7") then
112                 return RedWriter()
113         end
114         
115         -- Detect MagicBox
116         if fs.readfile("/proc/cpuinfo"):find("MagicBox") then
117                 return CommonWriter() 
118         end
119 end
120
121
122
123 Writer.COPY = {"/sbin/mtd"}
124 Writer.MTD = "/tmp/mtd"
125 Writer.IMAGEFIFO = "/tmp/mtdimage.fifo"
126
127
128 function Writer.write_block(self, name, imagestream, appendfile)
129         assert(self.blocks[name], ERROR_NOTFOUND)
130         local block = self.blocks[name]
131         local device = block.device
132         device = fs.stat(device) and device or self:_find_mtdblock(device)
133         assert(device, ERROR_NODETECT)
134         if block.magic then
135                 imagestream = self:_check_magic(imagestream, block.magic)
136         end
137         assert(imagestream, ERROR_INVMAGIC)
138         
139         self:_prepare_env()
140         
141         if appendfile then
142                 if block.write == WRITE_COMBINED then
143                         return (self:_write_combined(device, imagestream, appendfile) == 0)
144                 elseif block.write == WRITE_EMULATED then
145                         return (self:_write_emulated(device, imagestream, appendfile) == 0)
146                 else
147                         error(ERROR_NOTAVAIL)
148                 end
149         else
150                 return (self:_write_memory(device, imagestream) == 0)
151         end
152 end
153
154 function Writer._prepare_env(self)
155         if self._prepared then
156                 return
157         end
158         
159         for k, app in ipairs(self.COPY) do
160                 local target = "/tmp/"..fs.basename(app)
161                 fs.unlink(target)
162                 fs.copy(app, target)
163                 fs.chmod(target, "rwx------")
164         end
165         
166         self._prepared = true
167 end
168
169 function Writer._check_magic(self, imagestream, magic)
170         magic = type(magic) == "table" and magic or {magic}
171         
172         local block = imagestream()
173         assert(block, ERROR_NOSTREAM)
174         local cm = "%x%x" % {block:byte(1), block:byte(2)}
175         
176         if util.contains(magic, cm) then
177                 return ltn12.source.cat(ltn12.source.string(block), imagestream)
178         end
179 end
180
181 function Writer._find_mtdblock(self, mtdname)
182         local k
183         local prefix = "/dev/mtd"
184         prefix = prefix .. (fs.stat(prefix) and "/" or "")
185         
186         for l in io.lines("/proc/mtd") do
187                 local k = l:match('mtd([%%w-_]+):.*"%s"' % mtdname)
188                 if k then return prefix..k end
189         end
190 end
191
192 function Writer._write_emulated(self, devicename, imagestream, appendfile)
193         local stat = (self:_write_memory(device, imagestream) == 0)
194         stat = stat and (self:_refresh_block("rootfs") == 0)
195         local squash = self:_find_mtdblock("rootfs_data")
196         if squash then
197                 stat = stat and (self:_append("rootfs_data", imagestream, true) == 0)
198         else
199                 stat = stat and (self:_append("rootfs", imagestream) == 0)
200         end
201         return stat
202 end
203
204 function Writer._write_memory(self, devicename, imagestream)
205         local imageproc = posix.fork()
206         assert(imageproc ~= -1, ERROR_RESOURCE)
207         if imageproc == 0 then
208                 fs.unlink(self.IMAGEFIFO)
209                 assert(posix.mkfifo(self.IMAGEFIFO), ERROR_RESOURCE)
210                 local imagefifo = io.open(self.IMAGEFIFO, "w")
211                 assert(imagefifo, ERROR_RESOURCE)
212                 ltn12.pump.all(imagestream, ltn12.sink.file(imagefifo))
213                 os.exit(0)
214         end
215         
216         return os.execute( 
217                 "%s write '%s' '%s' >/dev/null 2>&1" % {
218                 self.MTD, self.IMAGEFIFO, devicename
219                 }
220         )       
221 end
222
223 function Writer._write_combined(self, devicename, imagestream, appendfile)
224         local imageproc = posix.fork()
225         assert(imageproc ~= -1, ERROR_RESOURCE)
226         if imageproc == 0 then
227                 fs.unlink(self.IMAGEFIFO)
228                 assert(posix.mkfifo(self.IMAGEFIFO), ERROR_RESOURCE)
229                 local imagefifo = io.open(self.IMAGEFIFO, "w")
230                 assert(imagefifo, ERROR_RESOURCE)
231                 ltn12.pump.all(imagestream, ltn12.sink.file(imagefifo))
232                 os.exit(0)
233         end
234         
235         return os.execute( 
236                 "%s -j '%s' write '%s' '%s' >/dev/null 2>&1" % {
237                         self.MTD, appendfile, self.IMAGEFIFO, devicename
238                 }
239         )
240 end
241
242 function Writer._refresh_block(self, devicename)
243         return os.execute("%s refresh '%s' >/dev/null 2>&1" % {self.MTD, devicename})
244 end
245
246 function Writer._append(self, devicename, appendfile, erase)
247         erase = erase and ("-e '%s' " % devicename) or ''
248         
249         return os.execute( 
250                 "%s %s jffs2write '%s' '%s' >/dev/null 2>&1" % {
251                         self.MTD, erase, appendfile, devicename
252                 }
253         )
254 end