Hi Taco, when writing scripts in texlua, I often have to find out whether a file or directory is readable or writable. The permission flags I get from lfs.attributes() are not sufficient. Though I can determine the UID and GID of the file owner, I still have to find out my own UID and GID and whether I'm member of a particular group. This is difficult, sometimes impossible (without access to getpwent(3) and getgrent(3)), and quite system dependent. In order to test whether a file is readable I currently open this file in a protected call. This is also possible for directories when I run lfs.dir() inside pcall(). It's not very elegant, and I still have no idea how to test whether a file is writable. I could try to open the file in append mode, but I fear that if the file is writable, this test would change the time stamps. Not very elegant either. I'm not aware of a better solution, but maybe I'm missing something. If not, I propose a new function lfs.access() which takes a file name and a string as an argument containing any combination of the letters 'r', 'w', or 'x' and returns the result of the system call access(2) (as a boolean). For instance, lfs.access(somefile, "rx") would then return the result of the system call access(somefile, R_OK|X_OK). On Windows there is a function _access() in the runtime lib which is similar to access(2) on Unix, though the executable flag isn't supported. Maybe it can be ignored. On the other hand it would be very convenient if the function returns true if the file is in PATH and its extension is in PATHEXT (as described in the MSVCRT sources). This makes the function call more expensive, but I think that access(2) is relatively expensive on Unix too because of the UID/GID lookups. I first considered to propose to extend lfs.isfile() and lfs.isdir() by an optional argument, but because lfs.isfile() checks whether a file is a regular file (which is fine), this seems to impose an unnecessary limitation: print(lfs.isfile('/dev/null')) => false print(lfs.attributes('/dev/null', 'mode')) => char device Hence, I think that a separate function lfs.access() makes more sense. WDYT? Regards, Reinhard -- ---------------------------------------------------------------------------- Reinhard Kotucha Phone: +49-511-3373112 Marschnerstr. 25 D-30167 Hannover mailto:reinhard.kotucha@web.de ---------------------------------------------------------------------------- Microsoft isn't the answer. Microsoft is the question, and the answer is NO. ----------------------------------------------------------------------------
On 5-2-2011 9:11, Reinhard Kotucha wrote:
In order to test whether a file is readable I currently open this file in a protected call. This is also possible for directories when I run lfs.dir() inside pcall(). It's not very elegant, and I still have no idea how to test whether a file is writable. I could try to open the file in append mode, but I fear that if the file is writable, this test would change the time stamps. Not very elegant either.
I use something function file.is_writable(name) local d = string.match(name,"^(.+)[/\\].-$") or "." local a = lfs.attributes(name) or lfs.attributes(d) return a and string/sub(a.permissions,2,2) == "w" end function file.is_readable(name) local a = lfs.attributes(name) return a and string.sub(a.permissions,1,1) == "r" end ----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | voip: 087 875 68 74 | www.pragma-ade.com | www.pragma-pod.nl -----------------------------------------------------------------
On 6 February 2011 Hans Hagen wrote:
On 5-2-2011 9:11, Reinhard Kotucha wrote:
In order to test whether a file is readable I currently open this file in a protected call. This is also possible for directories when I run lfs.dir() inside pcall(). It's not very elegant, and I still have no idea how to test whether a file is writable. I could try to open the file in append mode, but I fear that if the file is writable, this test would change the time stamps. Not very elegant either.
I use something
function file.is_writable(name) local d = string.match(name,"^(.+)[/\\].-$") or "." local a = lfs.attributes(name) or lfs.attributes(d) return a and string/sub(a.permissions,2,2) == "w" end
function file.is_readable(name) local a = lfs.attributes(name) return a and string.sub(a.permissions,1,1) == "r" end
Consider the following situation on Unix: $ whoami reinhard $ ls -l /etc/passwd -rw-r--r-- 1 root root 2269 Dec 19 14:57 /etc/passwd Your test suggests that the file is writable, but this is only true for root, not for the user who is running the script. You actually have to determine whether you are the owner of the file, or, if the file is group writable, whether you are member of this group, or whether the file is writable by everybody. Regarding Windows, I'm not sure. As far as I can see from the Lua sources, permission flags are directly derived from a stat() call. This means that they are related to the file owner too, not to the current user. If there is a file which is owned by the admin and is _not_ writable by normal users, does your test really return 'false' if run by a non-privileged user on Windows? Regards, Reinhard -- ---------------------------------------------------------------------------- Reinhard Kotucha Phone: +49-511-3373112 Marschnerstr. 25 D-30167 Hannover mailto:reinhard.kotucha@web.de ---------------------------------------------------------------------------- Microsoft isn't the answer. Microsoft is the question, and the answer is NO. ----------------------------------------------------------------------------
On 6-2-2011 3:56, Reinhard Kotucha wrote:
$ whoami reinhard $ ls -l /etc/passwd -rw-r--r-- 1 root root 2269 Dec 19 14:57 /etc/passwd
Your test suggests that the file is writable, but this is only true for root, not for the user who is running the script. You actually have to determine whether you are the owner of the file, or, if the file is group writable, whether you are member of this group, or whether the file is writable by everybody.
Isn't it a bug in the lfs library then? When is the point in returning an attribute that has no defined meaning?
Regarding Windows, I'm not sure. As far as I can see from the Lua sources, permission flags are directly derived from a stat() call. This means that they are related to the file owner too, not to the current user.
I suppose that we're only interested in the current user, so lfs.attributes should return the user's attributes. I was always under the impression that this was the case.
If there is a file which is owned by the admin and is _not_ writable by normal users, does your test really return 'false' if run by a non-privileged user on Windows?
I don't know. Whatever we end up with it should be robust or at least predictable. (i normally have only one account on computers and run everything as administrator or root) Hans ----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | voip: 087 875 68 74 | www.pragma-ade.com | www.pragma-pod.nl -----------------------------------------------------------------
On 6 February 2011 Hans Hagen wrote:
On 6-2-2011 3:56, Reinhard Kotucha wrote:
$ whoami reinhard $ ls -l /etc/passwd -rw-r--r-- 1 root root 2269 Dec 19 14:57 /etc/passwd
Your test suggests that the file is writable, but this is only true for root, not for the user who is running the script. You actually have to determine whether you are the owner of the file, or, if the file is group writable, whether you are member of this group, or whether the file is writable by everybody.
Isn't it a bug in the lfs library then? When is the point in returning an attribute that has no defined meaning?
No, it's not a bug. The name "attributes" is misleading, "stat" would be better. It *has* a defined meaning, it just returns what's stored in the stat structure of a particular file: struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ }; This is exactly the information you see in directory listings. lfs.attributes supports the stat() system call directly, except that st_blksize and st_blocks are not defined on Windows and that st_mode is split up into "permissions" and "mode".
Regarding Windows, I'm not sure. As far as I can see from the Lua sources, permission flags are directly derived from a stat() call. This means that they are related to the file owner too, not to the current user.
I suppose that we're only interested in the current user, so lfs.attributes should return the user's attributes. I was always under the impression that this was the case.
I think that lfs.attributes is correct, despite its unfortunate name. I proposed lfs.access() because access() and stat() are two distinct system calls anyway.
If there is a file which is owned by the admin and is _not_ writable by normal users, does your test really return 'false' if run by a non-privileged user on Windows?
I don't know. Whatever we end up with it should be robust or at least predictable.
I think that what I proposed is reliable and relatively easy to implement. At least there is an access() function in MSVCRT too which does the same as the one on Unix, though the executable bit is ignored. And if lfs.access(file, 'rx') then ... should be as robust and predictable as if (-r $file && -x $file) { ... in Perl. Regards, Reinhard -- ---------------------------------------------------------------------------- Reinhard Kotucha Phone: +49-511-3373112 Marschnerstr. 25 D-30167 Hannover mailto:reinhard.kotucha@web.de ---------------------------------------------------------------------------- Microsoft isn't the answer. Microsoft is the question, and the answer is NO. ----------------------------------------------------------------------------
On 7-2-2011 1:41, Reinhard Kotucha wrote:
No, it's not a bug. The name "attributes" is misleading, "stat" would be better. It *has* a defined meaning, it just returns what's stored in the stat structure of a particular file:
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ };
This is exactly the information you see in directory listings.
lfs.attributes supports the stat() system call directly, except that st_blksize and st_blocks are not defined on Windows and that st_mode is split up into "permissions" and "mode".
So where does the 'permissions' info in lfs.attributes come from then? I assume that that should reflect access. Hans ----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | voip: 087 875 68 74 | www.pragma-ade.com | www.pragma-pod.nl -----------------------------------------------------------------
On 02/07/2011 11:42 AM, Hans Hagen wrote:
So where does the 'permissions' info in lfs.attributes come from then? I assume that that should reflect access.
The permissions key list access possibilities for the user, the user's group, and all others. That is why there are three sets of values. What we do not have is a method to ascertain whether we are in fact 'user', 'user's group', or 'others'. Best wishes, Taco
participants (3)
-
Hans Hagen
-
Reinhard Kotucha
-
Taco Hoekwater