Cygwin or Windows: file permission functions are broken

T

Tim McDaniel

My apologies for asking a Cygwin- or Windows-specific question here,
but it's also Perl-specific, and I'm not sure of a better
easy-to-access place.

I looked in perlfaq5 and didn't see it discussed there, possibly
because it's a Cygwin or Windows problem in particular -- but this HAS
to be a common problem. I tried some Googling, but it's hard to
search for "w", and I couldn't think of effective search terms.

I'm using a pretty recent Cygwin, but I don't know how to find out a
version number or date. "cd"ed into an NTFS partition. Environment
variable CYGWIN is "tty ntsec".

The current directory is owned by group Administrators; my user ID is
in group Administrators; group Administrators has full control. So I
can do any regular commands that create or delete files (Cygwin touch,
CMD.EXE copy, anything).

But
perl -e 'print(-w "." ? "yes\n" : "no\n")'
prints "no", and the same for -r and -x.
Possibly related to "ls -ld" outputting this:
drwx------+ 30 ???????? none 0 Jan 15 11:44 .

I noticed this when I couldn't get File::Temp to work with DIR=>'.'
[footnote 2], because of its code

# Check that the parent directories exist
# Do this even for the case where we are simply returning a name
# not a file -- no point returning a name that includes a directory
# that does not exist or is not writable

unless (-d $parent) {
${$options{ErrStr}} = "Parent directory ($parent) is not a directory";
return ();
}
unless (-w $parent) {
${$options{ErrStr}} = "Parent directory ($parent) is not writable\n";
return ();
}
[footnote 1]


Question 1: is there any way I can get Perl's -r / -w / -x functions
to work? Is this indeed an FAQ question, or should I actually file a
bug report with the Cygwin group?

Question 2: Is there another module that's shipped with Perl that I
could use instead of File::Temp? I don't know of a way to tell all
the modules that are installed. (I'd really prefer not to depend on
yet another module needing to be installed.) IO::File::new_tmpfile()
doesn't take a directory name [2]. POSIX's
char * tempnam(const char *tmpdir, const char *prefix)
is not implemented in module POSIX::...

I guess I'll just adapt the code from perlfaq5 ...

Question 3: ... but why is it wrapped in a BEGIN block?


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[1] I consider that a design flaw. I believe that the best way to
test that an operation will succeed is simply to try to do it, and
catch the failure. (When the operation indicates all errors, and when
it is atomic, either succeeding completely or failing with no
residure.)

[2] There are reasons that I don't use TMPDIR, TEMP, or TMP. They are
irrelevant to this problem. Really. ...

.... if you insist: I often run the Perl program outside of a Cygwin
window. Even when TMPDIR is set in Windows syntax as C:\tmp, on
startup Perl transmogrifies it to /tmp (or whatever, per cygpath -u).
The Perl program calls non-Cygwin-aware programs, so they have to get
non-Cygwin paths like "e:\foo\bar" instead of "/CM/foo/bar". Since
the Perl program is running in a cmd.exe window, Cygwin programs are
not in the Path ... meaning I can't just run "cygpath -w". I don't
want to hard-code Path directories into the script.

But "." is a directory name that works in both Cygwin and non-Cygwin.
It's not much of a stretch to insist that they run the program in a
writable directory ... as long as Perl realizes that it's writable,
dammit!!1!
 
T

Thrill5

Tim McDaniel said:
My apologies for asking a Cygwin- or Windows-specific question here,
but it's also Perl-specific, and I'm not sure of a better
easy-to-access place.

I looked in perlfaq5 and didn't see it discussed there, possibly
because it's a Cygwin or Windows problem in particular -- but this HAS
to be a common problem. I tried some Googling, but it's hard to
search for "w", and I couldn't think of effective search terms.

I'm using a pretty recent Cygwin, but I don't know how to find out a
version number or date. "cd"ed into an NTFS partition. Environment
variable CYGWIN is "tty ntsec".

The current directory is owned by group Administrators; my user ID is
in group Administrators; group Administrators has full control. So I
can do any regular commands that create or delete files (Cygwin touch,
CMD.EXE copy, anything).

But
perl -e 'print(-w "." ? "yes\n" : "no\n")'
prints "no", and the same for -r and -x.
Possibly related to "ls -ld" outputting this:
drwx------+ 30 ???????? none 0 Jan 15 11:44 .

I noticed this when I couldn't get File::Temp to work with DIR=>'.'
[footnote 2], because of its code

# Check that the parent directories exist
# Do this even for the case where we are simply returning a name
# not a file -- no point returning a name that includes a directory
# that does not exist or is not writable

unless (-d $parent) {
${$options{ErrStr}} = "Parent directory ($parent) is not a
directory";
return ();
}
unless (-w $parent) {
${$options{ErrStr}} = "Parent directory ($parent) is not writable\n";
return ();
}
[footnote 1]


Question 1: is there any way I can get Perl's -r / -w / -x functions
to work? Is this indeed an FAQ question, or should I actually file a
bug report with the Cygwin group?

Question 2: Is there another module that's shipped with Perl that I
could use instead of File::Temp? I don't know of a way to tell all
the modules that are installed. (I'd really prefer not to depend on
yet another module needing to be installed.) IO::File::new_tmpfile()
doesn't take a directory name [2]. POSIX's
char * tempnam(const char *tmpdir, const char *prefix)
is not implemented in module POSIX::...

I guess I'll just adapt the code from perlfaq5 ...

Question 3: ... but why is it wrapped in a BEGIN block?


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[1] I consider that a design flaw. I believe that the best way to
test that an operation will succeed is simply to try to do it, and
catch the failure. (When the operation indicates all errors, and when
it is atomic, either succeeding completely or failing with no
residure.)

[2] There are reasons that I don't use TMPDIR, TEMP, or TMP. They are
irrelevant to this problem. Really. ...

... if you insist: I often run the Perl program outside of a Cygwin
window. Even when TMPDIR is set in Windows syntax as C:\tmp, on
startup Perl transmogrifies it to /tmp (or whatever, per cygpath -u).
The Perl program calls non-Cygwin-aware programs, so they have to get
non-Cygwin paths like "e:\foo\bar" instead of "/CM/foo/bar". Since
the Perl program is running in a cmd.exe window, Cygwin programs are
not in the Path ... meaning I can't just run "cygpath -w". I don't
want to hard-code Path directories into the script.

But "." is a directory name that works in both Cygwin and non-Cygwin.
It's not much of a stretch to insist that they run the program in a
writable directory ... as long as Perl realizes that it's writable,
dammit!!1!

I'm running ActiveState 5.8.8 and my Perl doesn't act like your version.
C:\Documents and Settings\Administrator>perl -e "print (-w '.' ? 'yes' :
'no')"
yes
C:\Documents and Settings\Administrator>perl -e "print (-d '.' ? 'yes' :
'no')"
yes
C:\Documents and Settings\Administrator>perl -e "print (-r '.' ? 'yes' :
'no')"
yes

I have also never seen any paths "transmogrified" by Perl. Have you tried
installing and running your program using an ActiveState build instead of a
version compiled under Cygwin?

Not sure what your issue with File::Temp is, but this is a standard module
included in the ActiveState distribution and works just like the Unix
version.
 
A

Andrew DeFaria

Tim said:
My apologies for asking a Cygwin- or Windows-specific question here,
but it's also Perl-specific, and I'm not sure of a better
easy-to-access place. Try the Cygwin mailing list.
I looked in perlfaq5 and didn't see it discussed there, possibly
because it's a Cygwin or Windows problem in particular -- but this HAS
to be a common problem. I tried some Googling, but it's hard to search
for "w", and I couldn't think of effective search terms.

I'm using a pretty recent Cygwin, but I don't know how to find out a
version number or date. "cd"ed into an NTFS partition. Environment
variable CYGWIN is "tty ntsec". IMHO "tty" is bad...
The current directory is owned by group Administrators; my user ID is
in group Administrators; group Administrators has full control. So I
can do any regular commands that create or delete files (Cygwin touch,
CMD.EXE copy, anything).

But
perl -e 'print(-w "." ? "yes\n" : "no\n")'
prints "no", and the same for -r and -x.
Possibly related to "ls -ld" outputting this:
drwx------+ 30 ???????? none 0 Jan 15 11:44 .
The "+" above indicates that the permissions are "special". Stated
differently, the Windows permissions mask is way larger of a set than
the Unix permissions mask. The "+" is saying that there are Windows
permissions to this directory (or file) that simply cannot be
represented in POSIX file permissions. This is probably the key to your
misunderstanding.
I noticed this when I couldn't get File::Temp to work with DIR=>'.'
[footnote 2], because of its code

# Check that the parent directories exist
# Do this even for the case where we are simply returning a name
# not a file -- no point returning a name that includes a directory
# that does not exist or is not writable

unless (-d $parent) {
${$options{ErrStr}} = "Parent directory ($parent) is not a directory";
return ();
}
unless (-w $parent) {
${$options{ErrStr}} = "Parent directory ($parent) is not writable\n";
return ();
}
[footnote 1]


Question 1: is there any way I can get Perl's -r / -w / -x functions
to work? Is this indeed an FAQ question, or should I actually file a
bug report with the Cygwin group?
Change the permissions of the directory such that they fall within
possibility of the simplistic POSIX standard.
Question 2: Is there another module that's shipped with Perl that I
could use instead of File::Temp? I don't know of a way to tell all the
modules that are installed. (I'd really prefer not to depend on yet
another module needing to be installed.) IO::File::new_tmpfile()
doesn't take a directory name [2]. POSIX's char * tempnam(const char
*tmpdir, const char *prefix) is not implemented in module POSIX::...
I believe File::Temp will use $TMP. My $TMP -> C:\Cygwin\tmp, or in
POSIX terms /tmp in Cygwin. My /tmp says:

$ ll -d /tmp
drwxrwxrwt 3 Andrew DeFaria None 0 Jan 16 00:05 /tmp/

No "+".
I guess I'll just adapt the code from perlfaq5 ...

Question 3: ... but why is it wrapped in a BEGIN block? BEGIN?
[2] There are reasons that I don't use TMPDIR, TEMP, or TMP. They are
irrelevant to this problem. Really. ...
Actually I see them as a viable alternative.
... if you insist: I often run the Perl program outside of a Cygwin
window.
Running within a Cygwin "window" (Whatever that is) or not is not the issue.
Even when TMPDIR is set in Windows syntax as C:\tmp, on startup Perl
transmogrifies it to /tmp (or whatever, per cygpath -u).
I think you're confused. There is not C:\tmp in Cygwin terms. There is
only /tmp. /tmp *translates* (not transmogrifies) to C:\Cygwin\tmp and
always has. Said differently, your C:\tmp is not Cygwin's notion of $TMP.
The Perl program calls non-Cygwin-aware programs, so they have to get
non-Cygwin paths like "e:\foo\bar" instead of "/CM/foo/bar".
However many Windows programs understand the syntax of C:/foo/bar
Since the Perl program is running in a cmd.exe window, Cygwin programs
are not in the Path ... meaning I can't just run "cygpath -w". I don't
want to hard-code Path directories into the script.
You can, and IMHO, should, add C:\Cygwin\bin to your user level PATH
environment variable. Again, IMHO it's a very reasonable and
understandable thing to do and require. Again, MHO.
But "." is a directory name that works in both Cygwin and non-Cygwin.
It's not much of a stretch to insist that they run the program in a
writable directory ... as long as Perl realizes that it's writable,
dammit!!1!
The definition of "writable" differs between the POSIX world and the
Windows world. That's the essence of your misunderstanding.
 
T

Tim McDaniel

I'm running ActiveState 5.8.8 and my Perl doesn't act like your version.
C:\Documents and Settings\Administrator>perl -e "print (-w '.' ? 'yes' :
'no')"
yes

&c.

Thanks for the datum. I'll consider installing ActiveState, though I
don't like the idea of two Perls.
I have also never seen any paths "transmogrified" by Perl.

It's Cygwin's perl port doing it: when it starts the program, it
converts TMP, TEMP, TMPDIR, PATH, and probably certain other
environment variables to Cygwin path syntax (UNIXy). It's not every
variable: I set FROG to 'C:\home' and it remains unchanged.
Not sure what your issue with File::Temp is,

Not so much of one. My main beef is with -r / -w being inaccurate.

tmpdir in File::Temp (specifically, the Cygwin.pm subclass) finds the
first writable directory (as determined by -w) in

$ENV{TMPDIR}
/tmp
C:/temp

As mentioned, Cygwin mutates TMPDIR into its own filename syntax. For
example, on my system it changed c:\tmp\1 into /tmp/1. That all works
fine, except

- when -w gives an incorrect answer, you get a different tmpdir. In
particular, I had write permission to /tmp/1, yet "-w '/tmp/1'"
returned false, so it went on to /tmp.

- when you have to run programs that require Windows paths, you need
to run the filename through "cygpath -w" to get the Windows syntax
again, and for increasingly silly-looking reasons I'm reluctant to
do that.
 
T

Tim McDaniel

IMHO "tty" is bad...

<http://www.cygwin.com/cygwin-ug-net/using-cygwinenv.html> explains
it. I don't find the explanation very clear (does "the Windows
console" refer to a cmd window or what?, but I'll look at it some
more.
The "+" above indicates that the permissions are "special". Stated
differently, the Windows permissions mask is way larger of a set than
the Unix permissions mask. The "+" is saying that there are Windows
permissions to this directory (or file) that simply cannot be
represented in POSIX file permissions. This is probably the key to
your misunderstanding.

You might have been kind enough to *explain* what you thought the
misunderstanding was. When I searched "man perlfunc" to quote you
the line that I remembered,

-w File is writable by effective uid/gid.

I also ran across lines that explained the problem:

The interpretation of the file permission operators "-r", "-R",
"-w", "-W", "-x", and "-X" is by default based solely on the mode
of the file and the uids and gids of the user. There may be other
reasons you can't actually read, write, or execute the file: for
example network filesystem access controls, ACLs (access control
lists), read-only filesystems, and unrecognized executable
formats. Note that the use of these six specific operators to
verify if some operation is possible is usually a mistake, because
it may be open to race conditions.

Also note that, for the superuser on the local filesystems, the
"-r", "-R", "-w", and "-W" tests always return 1, and "-x" and
"-X" return 1 if any execute bit is set in the mode. Scripts run
by the superuser may thus need to do a stat() to determine the
actual mode of the file, or temporarily set their effective uid to
something else.

If you are using ACLs, there is a pragma called "filetest" that
may produce more accurate results than the bare stat() mode bits.
When under the "use filetest 'access'" the above-mentioned
filetests will test whether the permission can (not) be granted
using the access() family of system calls. Also note that the
"-x" and "-X" may under this pragma return true even if there are
no execute permission bits set (nor any extra execute permission
ACLs). This strangeness is due to the underlying system calls'
definitions. Note also that, due to the implementation of "use
filetest 'access'", the "_" special filehandle won't cache the
results of the file tests when this pragma is in effect. Read the
documentation for the "filetest" pragma for more information.

Specifically, _ *will* cache any other test bits, like -e, -d, and
such.

The "use filetest access" pragma does work on Cygwin (in the latest
regular version). I created a directory and made it read/write only
to Administrators. In it,

$ touch frog # no error, so it works OK
$ perl -e 'print (-w "." ? "yes\n" : "no\n")'
no
$ perl -e 'use filetest access; print (-w "." ? "yes\n" : "no\n")'
yes

Unfortunately,
- File::Temp or File::Spec uses _ extensively
- "use filetest 'access'" has lexical scope, so it can't reach into
File::Temp or File::Spec

So I consider it a bug in File::Temp that I can't get around.
Change the permissions of the directory such that they fall within
possibility of the simplistic POSIX standard.

No, thank you. The permissions work well for my purposes as they are.

Yes, BEGIN, as in "the code from perlfaq5":

How do I make a temporary file name?

... If you're committed to creating a temporary file by hand, use
the process ID and/or the current time-value. If you need to have
many temporary files in one process, use a counter:

BEGIN {
use Fcntl;
my $temp_dir = -d '/tmp' ? '/tmp' : $ENV{TMPDIR} || $ENV{TEMP};
my $base_name = sprintf "%s/%d-%d-0000", $temp_dir, $$, time;

sub temp_file {
local *FH;
my $count = 0;
until( defined(fileno(FH)) || $count++ > 100 ) {
$base_name =~ s/-(\d+)$/"-" . (1 + $1)/e;
# O_EXCL is required for security reasons.
sysopen FH, $base_name, O_WRONLY|O_EXCL|O_CREAT;
}

if( defined fileno(FH) ) {
return (*FH, $base_name);
}
else {
return ();
}
}

}

When I implemented it, with a cache of filenames, a destroyer, and a
signal handler, it did not need to be in a BEGIN block.
Running within a Cygwin "window" (Whatever that is) or not is not the
issue.

It is an issue of why I don't use TMP, cygpath, and such.

By a "Cygwin window", I refer to rxvt or some other run of bash. In
..bashrc, I set a Cygwin-aware PATH. The programs I pick up are mostly
Cygwin-aware, in practice.

In a cmd.exe window, it has a Windows-aware Path, and I don't put
Cygwin programs into it. The programs I run want only Windows file
syntax.
I think you're confused. There is not C:\tmp in Cygwin terms. There
is only /tmp. /tmp *translates* (not transmogrifies) to C:\Cygwin\tmp

My apologies for being imprecise. /tmp actually translates to
[root of the Cygwin installation]\tmp. I set the root of the Cygwin
installation to C:\ instead of C:\cygwin, so for me, /tmp DOES
translate to C:\tmp. That's because I got too tired of having to
insert and delete "cygwin\" when converting paths by hand
(copying-and-pasting commands from one command window or file to
another), and to heck with setup.exe's deprecation warning.

And don't get snippy about informal / slangy terminology.
"transmogrify" is a just a sillier-sounding way of writing
"translate".
However many Windows programs understand the syntax of C:/foo/bar

But apparently not the syntax of just "/tmp", judging by the error
messages from Windows programs, and /tmp is what they were receiving
when I thought it was passing C:\tmp.
The definition of "writable" differs between the POSIX world and the
Windows world.

POSIX defines ACLs quite well. Say, rather, that Perl's
implementations of -r / -w / -x take a shortcut that works in practice
on most UNIXy systems, since few people there use ACLs, but produces
an unuseful effect on ACL-heavy systems.
 
T

Thrill5

Tim McDaniel said:
&c.

Thanks for the datum. I'll consider installing ActiveState, though I
don't like the idea of two Perls.
You don't need two Perls, only the ActiveState one. You can still run
Cygwin programs within ActiveState Perl, but without all the headaches that
you have experienced. The ActiveState version is a port that makes Perl
behave the same way on both unix and Win32 in as much this is possible.
Cygwin only emulates a POSIX enviroment and there are bigger differences
between unix and Win32 than just POSIX.
 
A

Andrew DeFaria

Thrill5 said:
You don't need two Perls, only the ActiveState one. You can still run
Cygwin programs within ActiveState Perl, but without all the headaches
that you have experienced. The ActiveState version is a port that
makes Perl behave the same way on both unix and Win32 in as much this
is possible. Cygwin only emulates a POSIX enviroment and there are
bigger differences between unix and Win32 than just POSIX.
What does ActiveState Perl do when you call setsid?
 
T

Thrill5

Thrill5 wrote:
You don't need two Perls, only the ActiveState one. You can still run Cygwin programs within ActiveState Perl, but without all the headaches that you have experienced. The ActiveState version is a port that makes Perl behave the same way on both unix and Win32 in as much this is possible. Cygwin only emulates a POSIX enviroment and there are bigger differences between unix and Win32 than just POSIX.

What does ActiveState Perl do when you call setsid?
 
T

Thrill5

Thrill5 wrote:
You don't need two Perls, only the ActiveState one. You can still run Cygwin programs within ActiveState Perl, but without all the headaches that you have experienced. The ActiveState version is a port that makes Perl behave the same way on both unix and Win32 in as much this is possible. Cygwin only emulates a POSIX enviroment and there are bigger differences between unix and Win32 than just POSIX.

What does ActiveState Perl do when you call setsid?
 
T

Thrill5

Thrill5 wrote:
You don't need two Perls, only the ActiveState one. You can still run Cygwin programs within ActiveState Perl, but without all the headaches that you have experienced. The ActiveState version is a port that makes Perl behave the same way on both unix and Win32 in as much this is possible. Cygwin only emulates a POSIX enviroment and there are bigger differences between unix and Win32 than just POSIX.

What does ActiveState Perl do when you call setsid?
 
A

Andrew DeFaria

Thrill5 said:
"Andrew DeFaria" <[email protected] <mailto:[email protected]>>
wrote in message
What does ActiveState Perl do when you call setsid?
--
Andrew DeFaria <http://defaria.com>
Who was the first person to look at a cow and say, "I think I'll
squeeze these dangly things here and drink whatever comes out"?

"setsid" is a POSIX function, not a built-in Perl function.
You didn't answer the question (three times in fact). The answer is that
it says it's not supported on this platform. Funny, Cygwin's Perl
manages it... Case closed.
 
T

Thrill5

Thrill5 wrote:

Thrill5 wrote:
You don't need two Perls, only the ActiveState one. You can still run Cygwin programs within ActiveState Perl, but without all the headaches that you have experienced. The ActiveState version is a port that makes Perl behave the same way on both unix and Win32 in as much this is possible. Cygwin only emulates a POSIX enviroment and there are bigger differences between unix and Win32 than just POSIX.

What does ActiveState Perl do when you call setsid?

--
Andrew DeFaria
Who was the first person to look at a cow and say, "I think I'll squeeze these dangly things here and drink whatever comes out"?
"setsid" is a POSIX function, not a built-in Perl function.
You didn't answer the question (three times in fact). The answer is that it says it's not supported on this platform. Funny, Cygwin's Perl manages it... Case closed.

--
Andrew DeFaria
I am in shape. Round is a shape!
Sorry about the triple post but I kept getting a "send" error when originally error replying.

Since Windows is not POSIX compliant, your can't "setsid" on Windows. If your writting cross-platform applications, you shouldn't be using functions that don't work on all the platforms your writting code for. A simple way of doing "setsid" on windows:

sub setsid {
if ($^O eq "MSWin32") {
require Win32::GUI;
my ($win) = Win32::GUI::GetPerlWindow();
Win32::GUI::Hide($win);
} else {
require POSIX;
POSIX::setsid();
}
}

TMTOWTDI!!!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top