Make luci.fs.rename work across fielsystems
[project/luci.git] / libs / core / luasrc / fs.lua
1 --[[
2 LuCI - Filesystem tools
3
4 Description:
5 A module offering often needed filesystem manipulation functions
6
7 FileId:
8 $Id$
9
10 License:
11 Copyright 2008 Steven Barth <steven@midlink.org>
12
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at
16
17         http://www.apache.org/licenses/LICENSE-2.0
18
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
24
25 ]]--
26
27 local io    = require "io"
28 local os    = require "os"
29 local ltn12 = require "luci.ltn12"
30 local posix = require "posix"
31
32 local type  = type
33
34 --- LuCI filesystem library.
35 module "luci.fs"
36
37 --- Test for file access permission on given path.
38 -- @class               function
39 -- @name                access
40 -- @param str   String value containing the path
41 -- @return              Number containing the return code, 0 on sucess or nil on error
42 -- @return              String containing the error description (if any)
43 -- @return              Number containing the os specific errno (if any)
44 access = posix.access
45
46 --- Evaluate given shell glob pattern and return a table containing all matching
47 -- file and directory entries.
48 -- @class                       function
49 -- @name                        glob
50 -- @param filename      String containing the path of the file to read
51 -- @return                      Table containing file and directory entries or nil if no matches
52 -- @return                      String containing the error description (if no matches)
53 -- @return                      Number containing the os specific errno (if no matches)
54 glob = posix.glob
55
56 --- Checks wheather the given path exists and points to a regular file.
57 -- @param filename      String containing the path of the file to read
58 -- @return                      Boolean indicating wheather given path points to regular file
59 function isfile(filename)
60         return posix.stat(filename, "type") == "regular"
61 end
62
63 --- Read the whole content of the given file into memory.
64 -- @param filename      String containing the path of the file to read
65 -- @return                      String containing the file contents or nil on error
66 -- @return                      String containing the error message on error
67 function readfile(filename)
68         local fp, err = io.open(filename)
69
70         if fp == nil then
71                 return nil, err
72         end
73
74         local data = fp:read("*a")
75         fp:close()
76         return data
77 end
78
79 --- Write the contents of given string to given file.
80 -- @param filename      String containing the path of the file to read
81 -- @param data          String containing the data to write
82 -- @return                      Boolean containing true on success or nil on error
83 -- @return                      String containing the error message on error
84 function writefile(filename, data)
85         local fp, err = io.open(filename, "w")
86
87         if fp == nil then
88                 return nil, err
89         end
90
91         fp:write(data)
92         fp:close()
93
94         return true
95 end
96
97 --- Copies a file.
98 -- @param source        Source file
99 -- @param dest          Destination
100 -- @return                      Boolean containing true on success or nil on error
101 function copy(source, dest)
102         return ltn12.pump.all(
103                 ltn12.source.file(io.open(source)),
104                 ltn12.sink.file(io.open(dest, "w"))
105         )
106 end
107
108 --- Renames a file.
109 -- @param source        Source file
110 -- @param dest          Destination
111 -- @return                      Boolean containing true on success or nil on error
112 function rename(source, dest)
113         local stat, err, code = os.rename(source, dest)
114         if code == 18 then
115                 stat, err, code = copy(source, dest)
116                 if stat then
117                         stat, err, code = unlink(source)
118                 end
119         end
120         return stat, err, code
121 end
122
123 --- Get the last modification time of given file path in Unix epoch format.
124 -- @param path  String containing the path of the file or directory to read
125 -- @return              Number containing the epoch time or nil on error
126 -- @return              String containing the error description (if any)
127 -- @return              Number containing the os specific errno (if any)
128 function mtime(path)
129         return posix.stat(path, "mtime")
130 end
131
132 --- Return the last element - usually the filename - from the given path with
133 -- the directory component stripped.
134 -- @class               function
135 -- @name                basename
136 -- @param path  String containing the path to strip
137 -- @return              String containing the base name of given path
138 -- @see                 dirname
139 basename = posix.basename
140
141 --- Return the directory component of the given path with the last element
142 -- stripped of.
143 -- @class               function
144 -- @name                dirname
145 -- @param path  String containing the path to strip
146 -- @return              String containing the directory component of given path
147 -- @see                 basename
148 dirname = posix.dirname
149
150 --- Return a table containing all entries of the specified directory.
151 -- @class               function
152 -- @name                dir
153 -- @param path  String containing the path of the directory to scan
154 -- @return              Table containing file and directory entries or nil on error
155 -- @return              String containing the error description on error
156 -- @return              Number containing the os specific errno on error
157 dir = posix.dir
158
159 --- Create a new directory, recursively on demand.
160 -- @param path          String with the name or path of the directory to create
161 -- @param recursive     Create multiple directory levels (optional, default is true)
162 -- @return                      Number with the return code, 0 on sucess or nil on error
163 -- @return                      String containing the error description on error
164 -- @return                      Number containing the os specific errno on error
165 function mkdir(path, recursive)
166         if recursive then
167                 local base = "."
168
169                 if path:sub(1,1) == "/" then
170                         base = ""
171                         path = path:gsub("^/+","")
172                 end
173
174                 for elem in path:gmatch("([^/]+)/*") do
175                         base = base .. "/" .. elem
176
177                         local stat = posix.stat( base )
178
179                         if not stat then
180                                 local stat, errmsg, errno = posix.mkdir( base )
181
182                                 if type(stat) ~= "number" or stat ~= 0 then
183                                         return stat, errmsg, errno
184                                 end
185                         else
186                                 if stat.type ~= "directory" then
187                                         return nil, base .. ": File exists", 17
188                                 end
189                         end
190                 end
191
192                 return 0
193         else
194                 return posix.mkdir( path )
195         end
196 end
197
198 --- Remove the given empty directory.
199 -- @class               function
200 -- @name                rmdir
201 -- @param path  String containing the path of the directory to remove
202 -- @return              Number with the return code, 0 on sucess or nil on error
203 -- @return              String containing the error description on error
204 -- @return              Number containing the os specific errno on error
205 rmdir = posix.rmdir
206
207 --- Get information about given file or directory.
208 -- @class               function
209 -- @name                stat
210 -- @param path  String containing the path of the directory to query
211 -- @return              Table containing file or directory properties or nil on error
212 -- @return              String containing the error description on error
213 -- @return              Number containing the os specific errno on error
214 stat = posix.stat
215
216 --- Set permissions on given file or directory.
217 -- @class               function
218 -- @name                chmod
219 -- @param path  String containing the path of the directory
220 -- @param perm  String containing the permissions to set ([ugoa][+-][rwx])
221 -- @return              Number with the return code, 0 on sucess or nil on error
222 -- @return              String containing the error description on error
223 -- @return              Number containing the os specific errno on error
224 chmod = posix.chmod
225
226 --- Create a hard- or symlink from given file (or directory) to specified target
227 -- file (or directory) path.
228 -- @class                       function
229 -- @name                        link
230 -- @param path1         String containing the source path to link
231 -- @param path2         String containing the destination path for the link
232 -- @param symlink       Boolean indicating wheather to create a symlink (optional)
233 -- @return                      Number with the return code, 0 on sucess or nil on error
234 -- @return                      String containing the error description on error
235 -- @return                      Number containing the os specific errno on error
236 link = posix.link
237
238 --- Remove the given file.
239 -- @class               function
240 -- @name                unlink
241 -- @param path  String containing the path of the file to remove
242 -- @return              Number with the return code, 0 on sucess or nil on error
243 -- @return              String containing the error description on error
244 -- @return              Number containing the os specific errno on error
245 unlink = posix.unlink
246
247 --- Retrieve target of given symlink.
248 -- @class               function
249 -- @name                readlink
250 -- @param path  String containing the path of the symlink to read
251 -- @return              String containing the link target or nil on error
252 -- @return              String containing the error description on error
253 -- @return              Number containing the os specific errno on error
254 readlink = posix.readlink