1--[[ 2 POSIX library for Lua 5.1, 5.2, 5.3 & 5.4. 3 Copyright (C) 2014-2021 Gary V. Vaughan 4]] 5--[[-- 6 Legacy Lua POSIX bindings. 7 8 APIs for maintaining compatibility with previous releases. 9 10 @module posix 11]] 12 13 14local LOG_MASK = require 'posix.syslog'.LOG_MASK 15local MODE_MAP = require 'posix._base'.MODE_MAP 16local O_CREAT = require 'posix.fcntl'.O_CREAT 17local O_TRUNC = require 'posix.fcntl'.O_TRUNC 18local O_WRONLY = require 'posix.fcntl'.O_WRONLY 19local S_IRGRP = require 'posix.sys.stat'.S_IRGRP 20local S_IROTH = require 'posix.sys.stat'.S_IROTH 21local S_IRUSR = require 'posix.sys.stat'.S_IRUSR 22local S_IRWXG = require 'posix.sys.stat'.S_IRWXG 23local S_IRWXO= require 'posix.sys.stat'.S_IRWXO 24local S_IRWXU = require 'posix.sys.stat'.S_IRWXU 25local S_ISGID = require 'posix.sys.stat'.S_ISGID 26local S_ISUID = require 'posix.sys.stat'.S_ISUID 27local S_IWGRP = require 'posix.sys.stat'.S_IWGRP 28local S_IWOTH = require 'posix.sys.stat'.S_IWOTH 29local S_IWUSR = require 'posix.sys.stat'.S_IWUSR 30local S_IXGRP = require 'posix.sys.stat'.S_IXGRP 31local S_IXOTH = require 'posix.sys.stat'.S_IXOTH 32local S_IXUSR = require 'posix.sys.stat'.S_IXUSR 33 34local argerror = require 'posix._base'.argerror 35local argscheck = require 'posix._base'.argscheck 36local chmod = require 'posix.sys.stat'.chmod 37local band = require 'posix._base'.band 38local bnot = require 'posix._base'.bnot 39local bor = require 'posix._base'.bor 40local concat = table.concat 41local gsub = string.gsub 42local match = string.match 43local mkdir = require 'posix.sys.stat'.mkdir 44local mkfifo = require 'posix.sys.stat'.mkfifo 45local msgget = require 'posix.sys.msg'.msgget 46local open = require 'posix.fcntl'.open 47local pushmode = require 'posix._base'.pushmode 48local setlogmask = require 'posix.syslog'.setlogmask 49local stat = require 'posix.sys.stat'.stat 50local sub = string.sub 51local tonumber = tonumber 52local umask = require 'posix.sys.stat'.umask 53 54 55local RWXALL = bor(S_IRWXU, S_IRWXG, S_IRWXO) 56local FCREAT = bor(O_CREAT, O_WRONLY, O_TRUNC) 57 58 59local function rwxrwxrwx(modestr) 60 local mode = 0 61 for i = 1, 9 do 62 if sub(modestr, i, i) == MODE_MAP[i].c then 63 mode = bor(mode, MODE_MAP[i].b) 64 elseif sub(modestr, i, i) == 's' then 65 if i == 3 then 66 mode = bor(mode, S_ISUID, S_IXUSR) 67 elseif i == 6 then 68 mode = bor(mode, S_ISGID, S_IXGRP) 69 else 70 return nil -- bad mode 71 end 72 end 73 end 74 return mode 75end 76 77 78local function octal_mode(modestr) 79 local mode = 0 80 for i = 1, #modestr do 81 mode = mode * 8 + tonumber(sub(modestr, i, i)) 82 end 83 return mode 84end 85 86 87local function mode_munch(mode, modestr) 88 if modestr == nil then 89 return nil, 'string expected, got no value' 90 elseif #modestr == 9 and match(modestr, '^[-rswx]+$') then 91 return rwxrwxrwx(modestr) 92 elseif match(modestr, '^[0-7]+$') then 93 return octal_mode(modestr) 94 elseif match(modestr, '^[ugoa]+%s*[-+=]%s*[rswx]+,*') then 95 gsub(modestr, '%s*(%a+)%s*(.)%s*(%a+),*', function(who, op, what) 96 local bits, bobs = 0, 0 97 if match(who, '[ua]') then 98 bits = bor(bits, S_ISUID, S_IRWXU) 99 end 100 if match(who, '[ga]') then 101 bits = bor(bits, S_ISGID, S_IRWXG) 102 end 103 if match(who, '[oa]') then 104 bits = bor(bits, S_IRWXO) 105 end 106 if match(what, 'r') then 107 bobs = bor(bobs, S_IRUSR, S_IRGRP, S_IROTH) 108 end 109 if match(what, 'w') then 110 bobs = bor(bobs, S_IWUSR, S_IWGRP, S_IWOTH) 111 end 112 if match(what, 'x') then 113 bobs = bor(bobs, S_IXUSR, S_IXGRP, S_IXOTH) 114 end 115 if match(what, 's') then 116 bobs = bor(bobs, S_ISUID, S_ISGID) 117 end 118 if op == '+' then 119 -- mode |= bits & bobs 120 mode = bor(mode, band(bits, bobs)) 121 elseif op == '-' then 122 -- mode &= ~(bits & bobs) 123 mode = band(mode, bnot(band(bits, bobs))) 124 elseif op == '=' then 125 -- mode =(mode & ~bits) |(bits & bobs) 126 mode = bor(band(mode, bnot(bits)), band(bits, bobs)) 127 end 128 end) 129 return mode 130 else 131 return nil, 'bad mode' 132 end 133end 134 135 136return { 137 --- Change the mode of the path. 138 -- @function chmod 139 -- @string path existing file path 140 -- @string mode one of the following formats: 141 -- 142 -- * 'rwxrwxrwx' (e.g. 'rw-rw-r--') 143 -- * 'ugo+-=rwx' (e.g. 'u+w') 144 -- * '+-=rwx' (e.g. '+w') 145 -- 146 -- @return[1] int `0`, if successful 147 -- @return[2] nil 148 -- @treturn[2] string error message 149 -- @treturn[2] int errnum 150 -- @see chmod(2) 151 -- @usage chmod('bin/dof', '+x') 152 chmod = argscheck('chmod(string, string)', function(path, modestr) 153 local mode = (stat(path) or {}).st_mode 154 local bits, err = mode_munch(mode or 0, modestr) 155 if bits == nil then 156 argerror('chmod', 2, err, 2) 157 end 158 return chmod(path, band(bits, RWXALL)) 159 end), 160 161 --- Create a file. 162 -- This function is obsoleted by @{posix.fcntl.open} with `posix.O_CREAT`. 163 -- @function creat 164 -- @string path name of file to create 165 -- @string mode permissions with which to create file 166 -- @treturn[1] int file descriptor of file at *path*, if successful 167 -- @return[2] nil 168 -- @treturn[2] string error message 169 -- @treturn[2] int errnum 170 -- @see creat(2) 171 -- @see posix.chmod 172 -- @usage 173 -- fd = creat('data', 'rw-r-----') 174 creat = argscheck('creat(string, string)', function(path, modestr) 175 local bits, err = mode_munch(0, modestr) 176 if bits == nil then 177 argerror('creat', 2, err, 2) 178 end 179 return open(path, FCREAT, band(bits, RWXALL)) 180 end), 181 182 --- Make a directory. 183 -- @function mkdir 184 -- @string path location in file system to create directory 185 -- @treturn[1] int `0`, if successful 186 -- @return[2] nil 187 -- @treturn[2] string error message 188 -- @treturn[2] int errnum 189 mkdir = argscheck('mkdir(string)', function(path) 190 return mkdir(path, RWXALL) 191 end), 192 193 --- Make a FIFO pipe. 194 -- @function mkfifo 195 -- @string path location in file system to create fifo 196 -- @treturn[1] int `0`, if successful 197 -- @return[2] nil 198 -- @treturn[2] string error message 199 -- @treturn[2] int errnum 200 mkfifo = argscheck('mkfifo(string)', function(path) 201 return mkfifo(path, RWXALL) 202 end), 203 204 --- Get a message queue identifier 205 -- @function msgget 206 -- @int key message queue id, or `IPC_PRIVATE` for a new queue 207 -- @int[opt=0] flags bitwise OR of zero or more from `IPC_CREAT` and `IPC_EXCL` 208 -- @string[opt='rw-rw-rw-'] mode execute bits are ignored 209 -- @treturn[1] int message queue identifier, if successful 210 -- @return[2] nil 211 -- @treturn[2] string error message 212 -- @treturn[2] int errnum 213 -- @see msgget(2) 214 msgget = argscheck('msgget(int, ?int, ?string)', function(key, msgflg, modestr) 215 local bits, err = mode_munch(0, modestr) 216 if bits == nil then 217 argerror('msgget', 3, err, 2) 218 end 219 return msgget(key, bor(msgflg, band(bits, RWXALL))) 220 end), 221 222 --- Open a file. 223 -- @function open 224 -- @string path file to act on 225 -- @int oflags bitwise OR of zero or more of `O_RDONLY`, `O_WRONLY`, `O_RDWR`, 226 -- `O_APPEND`, `O_CREAT`, `O_DSYNC`, `O_EXCL`, `O_NOCTTY`, `O_NONBLOCK`, 227 -- `O_RSYNC`, `O_SYNC`, `O_TRUNC` 228 -- @string modestr(used with `O_CREAT`; see @{chmod} for format) 229 -- @treturn[1] int file descriptor for *path*, if successful 230 -- @return[2] nil 231 -- @treturn[2] string error message 232 -- @treturn[2] int errnum 233 -- @see open(2) 234 -- @usage 235 -- fd = posix.open('data', bit.bor(posix.O_CREAT, posix.O_RDWR), 'rw-r-----') 236 open = argscheck('open(string, int, [string])', function(path, oflags, modestr) 237 local bits 238 if band(oflags, O_CREAT) ~= 0 then 239 bits, err = mode_munch(0, modestr) 240 if bits == nil then 241 argerror('open', 3, err, 2) 242 end 243 bits = band(bits, RWXALL) 244 end 245 return open(path, oflags, bits) 246 end), 247 248 --- Set log priority mask 249 -- @function setlogmask 250 -- @int ... zero or more of `LOG_EMERG`, `LOG_ALERT`, `LOG_CRIT`, 251 -- `LOG_WARNING`, `LOG_NOTICE`, `LOG_INFO` and `LOG_DEBUG` 252 -- @treturn[1] int `0`, if successful 253 -- @return[2] nil 254 -- @treturn[2] string error message 255 -- @treturn[2] int errnum 256 setlogmask = argscheck('setlogmask(?int...)', function(...) 257 local mask, i, t = 0, 1, {...} 258 while t[i] do 259 mask = bor(mask, LOG_MASK(t[i])) 260 i = i + 1 261 end 262 return setlogmask(mask) 263 end), 264 265 --- Set file mode creation mask. 266 -- @function umask 267 -- @string[opt] mode file creation mask string 268 -- @treturn string previous umask 269 -- @see umask(2) 270 -- @see posix.sys.stat.umask 271 umask = argscheck('umask(?string)', function(modestr) 272 modestr = modestr or '' 273 local mode = umask(0) 274 umask(mode) 275 mode = band(bnot(mode), RWXALL) 276 if modestr ~= '' then 277 local bits, err = mode_munch(mode, modestr) 278 if bits == nil then 279 argerror('umask', 1, err, 2) 280 end 281 mode = band(bits, RWXALL) 282 umask(bnot(mode)) 283 end 284 return pushmode(mode) 285 end), 286} 287