G'day Rob,
I tested the following using Cygwin
version 3.3.5
running on Win10 (both fully updated less than two days ago).
$ uname -a
CYGWIN_NT-10.0-19044 titan 3.3.5-341.x86_64 2022-05-13 12:27 UTC x86_6
+4 Cygwin
I don't have any old versions of Perl available; however,
I don't think there's any code here that wouldn't run on Perl 5.8
(happy to be corrected on that as I can't test it myself).
Some basic notes on Perl code (you probably know most of this; possibly useful for other readers):
-
stat gets the mode;
& 07777 masks off the file type leaving just the perm value
(e.g. get 0644 instead of 0100644).
-
sprintf provides the octal format you wanted.
-
chmod changes permissions using an octal number (stored in %perms).
-
I've recently taken to using the "system PROGRAM LIST" form of system
for security reasons.
However, for a long time, I've been using 'use autodie ':all';.
Unfortunately, these two don't play well together; CORE::system fixes this;
see "autodie: system/exec" for details.
-
As a side note, IPC::System::Simple has some useful functions
to avoid using system directly in code.
Here's perm_tasks.pl:
#!/usr/bin/env perl
use strict;
use warnings;
use autodie ':all';
use constant {
OLD => 0,
NEW => 1,
};
print "OS: $^O; Perl: $^V\n";
my @filenames = 'a' .. 'd';
my %perms = (
a => [0644, 0644],
b => [0644, 0755],
c => [0644, 0440],
d => [0644, 07644],
);
print "\n*** Initial permissions (visual check):\n";
list_perms(@filenames);
print "\n*** Initial permissions (program check):\n";
check_perms(\@filenames, \%perms, OLD);
change_perms(\@filenames, \%perms, NEW);
print "\n*** Modified permissions (visual check):\n";
list_perms(@filenames);
print "\n*** Modified permissions (program check):\n";
check_perms(\@filenames, \%perms, NEW);
change_perms(\@filenames, \%perms, OLD);
print "\n*** Restored permissions (visual check):\n";
list_perms(@filenames);
print "\n*** Restored permissions (program check):\n";
check_perms(\@filenames, \%perms, OLD);
sub list_perms {
my (@files) = @_;
my @cmd_list = (ls => '-l', @files);
CORE::system {$cmd_list[0]} @cmd_list;
return;
}
sub check_perms {
my ($files, $perms, $oldnew) = @_;
for my $file (@$files) {
my $perm = (stat $file)[2] & 07777;
my $expected
= sprintf('%#5o', $perms->{$file}[$oldnew]);
print
"File: '$file'; Perm: ",
sprintf('%#5o', $perm),
'; Check: ',
(
$perm == $perms->{$file}[$oldnew]
? 'OK' : "WRONG (expected: $expected)"
),
"\n";
}
return;
}
sub change_perms {
my ($files, $perms, $oldnew) = @_;
for my $file (@$files) {
chmod $perms->{$file}[$oldnew], $file;
}
return;
}
I initially created the four files manually (e.g. >a);
they all started with 0644 (rw-r--r--) according to my 0022 umask.
I then programmatically altered three of those;
'd' being quite exotic with 07644 (rwSr-Sr-T).
I then restored all to their original values;
'a' hadn't changed and restoring it (basically a no-op) was not a problem.
Output:
$ ./perm_tasks.pl
OS: cygwin; Perl: v5.36.0
*** Initial permissions (visual check):
-rw-r--r-- 1 ken None 0 Sep 4 02:07 a
-rw-r--r-- 1 ken None 0 Sep 4 02:07 b
-rw-r--r-- 1 ken None 0 Sep 4 02:07 c
-rw-r--r-- 1 ken None 0 Sep 4 02:16 d
*** Initial permissions (program check):
File: 'a'; Perm: 0644; Check: OK
File: 'b'; Perm: 0644; Check: OK
File: 'c'; Perm: 0644; Check: OK
File: 'd'; Perm: 0644; Check: OK
*** Modified permissions (visual check):
-rw-r--r-- 1 ken None 0 Sep 4 02:07 a
-rwxr-xr-x 1 ken None 0 Sep 4 02:07 b
-r--r----- 1 ken None 0 Sep 4 02:07 c
-rwSr-Sr-T 1 ken None 0 Sep 4 02:16 d
*** Modified permissions (program check):
File: 'a'; Perm: 0644; Check: OK
File: 'b'; Perm: 0755; Check: OK
File: 'c'; Perm: 0440; Check: OK
File: 'd'; Perm: 07644; Check: OK
*** Restored permissions (visual check):
-rw-r--r-- 1 ken None 0 Sep 4 02:07 a
-rw-r--r-- 1 ken None 0 Sep 4 02:07 b
-rw-r--r-- 1 ken None 0 Sep 4 02:07 c
-rw-r--r-- 1 ken None 0 Sep 4 02:16 d
*** Restored permissions (program check):
File: 'a'; Perm: 0644; Check: OK
File: 'b'; Perm: 0644; Check: OK
File: 'c'; Perm: 0644; Check: OK
File: 'd'; Perm: 0644; Check: OK
"On Cygwin, I find that sometimes the file permissions change when I (programmatically) make alterations to a text file."
I do find that execute permissions are automatically set for new files from a non-cygwin source
copied to somewhere under /cygdrive/c/cygwin64
(e.g. saving a PNG created by Gimp which I run under Win10;
downloading a PDF from the internet; copying a DOCX from $work, and so on).
Files that I create myself (under /cygdrive/c/cygwin64) do not get execute permissions.
I've not encountered the scenario you describe; although,
I've probably not done a lot of that (beyond appending to a logfile and such like).
I'd be happy to check or test if that would be useful.
Update (correction):
In the penultimate paragraph, I had /cygdrive/ in two places.
Those should have been, and I've now changed them to, /cygdrive/c/cygwin64.
By way of clarification for those unfamiliar with Cygwin:
Cygwin MSWin
============================= ====================
/cygdrive/c C:\
/cygdrive/d D:\
/cygdrive/c/Users/ken C:\Users\ken
/cygdrive/c/cygwin64 C:\cygwin64
/ C:\cygwin64
/cygdrive/c/cygwin64/home/ken C:\cygwin64\home\ken
/home/ken C:\cygwin64\home\ken