File indexing completed on 2025-01-19 12:45:15
0001 #!/usr/bin/perl -T 0002 use strict; 0003 0004 ######################################## 0005 # config files 0006 $nfs_exports::default_options = '*(ro,all_squash)'; 0007 $nfs_exports::conf_file = '/etc/exports'; 0008 $smb_exports::conf_file = '/etc/samba/smb.conf'; 0009 my $authorisation_file = '/etc/security/fileshare.conf'; 0010 my $authorisation_group = 'fileshare'; 0011 0012 0013 ######################################## 0014 # Copyright (C) 2001-2002 MandrakeSoft (pixel@mandriva.com) 0015 # 0016 # This program is free software; you can redistribute it and/or modify 0017 # it under the terms of the GNU General Public License as published by 0018 # the Free Software Foundation; either version 2, or (at your option) 0019 # any later version. 0020 # 0021 # This program is distributed in the hope that it will be useful, 0022 # but WITHOUT ANY WARRANTY; without even the implied warranty of 0023 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0024 # GNU General Public License for more details. 0025 # 0026 # You should have received a copy of the GNU General Public License 0027 # along with this program; if not, write to the Free Software 0028 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0029 0030 0031 ######################################## 0032 my $uid = $<; 0033 my $username = getpwuid($uid); 0034 0035 ######################################## 0036 # errors 0037 my $usage = 0038 "usage: fileshareset --add <dir> 0039 fileshareset --remove <dir>"; 0040 my $not_enabled = 0041 qq(File sharing is not enabled. 0042 To enable file sharing put 0043 "FILESHARING=yes" in $authorisation_file); 0044 0045 my $not_simple_enabled = 0046 qq(Simple file sharing is not enabled. 0047 To enable simple file sharing put 0048 "SHARINGMODE=simple" in $authorisation_file); 0049 0050 my $non_authorised = 0051 qq(You are not authorised to use file sharing 0052 To grant you the rights: 0053 - put "RESTRICT=no" in $authorisation_file 0054 - or put user "$username" in group "$authorisation_group"); 0055 0056 my $no_export_method = "can't export anything: no nfs, no smb"; 0057 0058 my %exit_codes = reverse ( 0059 1 => $non_authorised, 0060 2 => $usage, 0061 0062 # when adding 0063 3 => "already exported", 0064 4 => "invalid mount point", 0065 0066 # when removing 0067 5 => "not exported", 0068 0069 6 => $no_export_method, 0070 0071 7 => $not_enabled, 0072 0073 8 => $not_simple_enabled, 0074 0075 255 => "various", 0076 ); 0077 0078 ################################################################################ 0079 # correct PATH needed to call /etc/init.d/... ? seems not, but... 0080 %ENV = ();#(PATH => '/bin:/sbin:/usr/bin:/usr/sbin'); 0081 0082 my $modify = $0 =~ /fileshareset/; 0083 0084 authorisation::check($modify); 0085 0086 my @exports = ( 0087 -e $nfs_exports::conf_file ? nfs_exports::read() : (), 0088 -e $smb_exports::conf_file ? smb_exports::read() : (), 0089 ); 0090 @exports or error($no_export_method); 0091 0092 if ($modify) { 0093 my ($cmd, $dir) = @ARGV; 0094 $< = $>; 0095 @ARGV == 2 && ($cmd eq '--add' || $cmd eq '--remove') or error($usage); 0096 0097 verify_mntpoint($dir); 0098 0099 if ($cmd eq '--add') { 0100 my @errs = map { eval { $_->add($dir) }; $@ } @exports; 0101 grep { !$_ } @errs or error("already exported"); 0102 } else { 0103 my @errs = map { eval { $_->remove($dir) }; $@ } @exports; 0104 grep { !$_ } @errs or error("not exported"); 0105 } 0106 foreach my $export (@exports) { 0107 $export->write; 0108 $export->update_server; 0109 } 0110 } 0111 my @mntpoints = grep {$_} uniq(map { map { $_->{mntpoint} } @$_ } @exports); 0112 print "$_\n" foreach grep { own($_) } @mntpoints; 0113 0114 0115 sub own { $uid == 0 || (stat($_[0]))[4] == $uid } 0116 0117 sub verify_mntpoint { 0118 local ($_) = @_; 0119 my $ok = 1; 0120 $ok &&= m|^/|; 0121 $ok &&= !m|/\.\./|; 0122 $ok &&= !m|[\0\n\r]|; 0123 $ok &&= -d $_; 0124 $ok &&= own($_); 0125 $ok or error("invalid mount point"); 0126 } 0127 0128 sub error { 0129 my ($string) = @_; 0130 print STDERR "$string\n"; 0131 exit($exit_codes{$string} || 255); 0132 } 0133 sub member { my $e = shift; foreach (@_) { $e eq $_ and return 1 } 0 } 0134 sub uniq { my %l; $l{$_} = 1 foreach @_; grep { delete $l{$_} } @_ } 0135 0136 0137 ################################################################################ 0138 package authorisation; 0139 0140 sub read_conf { 0141 my ($exclusive_lock) = @_; 0142 open F_lock, $authorisation_file; # don't care if it's missing 0143 flock(F_lock, $exclusive_lock ? 2 : 1) or die "can't lock $authorisation_file"; 0144 my %conf; 0145 foreach (<F_lock>) { 0146 s/#.*//; # remove comments 0147 s/^\s+//; 0148 s/\s+$//; 0149 /^$/ and next; 0150 my ($cmd, $value) = split('=', $_, 2); 0151 $conf{$cmd} = $value || warn qq(suspicious line "$_" in $authorisation_file\n); 0152 } 0153 # no close F_lock, keep it locked 0154 \%conf 0155 } 0156 0157 sub check { 0158 my ($exclusive_lock) = @_; 0159 my $conf = read_conf($exclusive_lock); 0160 if (lc($conf->{FILESHARING}) eq 'no') { 0161 ::error($not_enabled); 0162 } 0163 0164 if (lc($conf->{SHARINGMODE}) eq 'advanced') { 0165 ::error($not_simple_enabled); 0166 } 0167 0168 if (lc($conf->{FILESHAREGROUP} ne '')) { 0169 $authorisation_group = lc($conf->{FILESHAREGROUP}); 0170 } 0171 0172 if (lc($conf->{RESTRICT}) eq 'no') { 0173 # ok, access granted for everybody 0174 } else { 0175 my @l; 0176 while (@l = getgrent) { 0177 last if $l[0] eq $authorisation_group; 0178 } 0179 ::member($username, split(' ', $l[3])) or ::error($non_authorised); 0180 } 0181 } 0182 0183 ################################################################################ 0184 package exports; 0185 0186 sub find { 0187 my ($exports, $mntpoint) = @_; 0188 foreach (@$exports) { 0189 $_->{mntpoint} eq $mntpoint and return $_; 0190 } 0191 undef; 0192 } 0193 0194 sub add { 0195 my ($exports, $mntpoint) = @_; 0196 foreach (@$exports) { 0197 $_->{mntpoint} eq $mntpoint and die 'add'; 0198 } 0199 push @$exports, my $e = { mntpoint => $mntpoint }; 0200 $e; 0201 } 0202 0203 sub remove { 0204 my ($exports, $mntpoint) = @_; 0205 my @l = grep { $_->{mntpoint} ne $mntpoint } @$exports; 0206 @l < @$exports or die 'remove'; 0207 @$exports = @l; 0208 } 0209 0210 0211 ################################################################################ 0212 package nfs_exports; 0213 0214 use vars qw(@ISA $conf_file $default_options); 0215 BEGIN { @ISA = 'exports' } 0216 0217 sub read { 0218 my $file = $conf_file; 0219 local *F; 0220 open F, $file or return []; 0221 0222 my ($prev_raw, $prev_line, %e, @l); 0223 my $line_nb = 0; 0224 foreach my $raw (<F>) { 0225 $line_nb++; 0226 local $_ = $raw; 0227 $raw .= "\n" if !/\n/; 0228 0229 s/#.*//; # remove comments 0230 0231 s/^\s+//; 0232 s/\s+$//; # remove unuseful spaces to help regexps 0233 0234 if (/^$/) { 0235 # blank lines ignored 0236 $prev_raw .= $raw; 0237 next; 0238 } 0239 0240 if (/\\$/) { 0241 # line continue across lines 0242 chop; # remove the backslash 0243 $prev_line .= "$_ "; 0244 $prev_raw .= $raw; 0245 next; 0246 } 0247 my $line = $prev_line . $_; 0248 my $raw_line = $prev_raw . $raw; 0249 ($prev_line, $prev_raw) = ('', ''); 0250 0251 my ($mntpoint, $options) = $line =~ /("[^"]*"|\S+)\s+(.*)/ or die "$file:$line_nb: bad line $line\n"; 0252 0253 # You can also specify spaces or any other unusual characters in the 0254 # export path name using a backslash followed by the character code as 0255 # 3 octal digits. 0256 $mntpoint =~ s/\\(\d{3})/chr(oct $1)/ge; 0257 0258 # not accepting weird characters that would break the output 0259 $mntpoint =~ m/[\0\n\r]/ and die "i won't handle this"; 0260 push @l, { mntpoint => $mntpoint, option => $options, raw => $raw_line }; 0261 } 0262 bless \@l, 'nfs_exports'; 0263 } 0264 0265 sub write { 0266 my ($nfs_exports) = @_; 0267 foreach (@$nfs_exports) { 0268 if (!exists $_->{options}) { 0269 $_->{options} = $default_options; 0270 } 0271 if (!exists $_->{raw}) { 0272 my $mntpoint = $_->{mntpoint} =~ /\s/ ? qq("$_->{mntpoint}") : $_->{mntpoint}; 0273 $_->{raw} = sprintf("%s %s\n", $mntpoint, $_->{options}); 0274 } 0275 } 0276 local *F; 0277 open F, ">$conf_file" or die "can't write $conf_file"; 0278 print F $_->{raw} foreach @$nfs_exports; 0279 } 0280 0281 sub update_server { 0282 if (fork) { 0283 system('/usr/sbin/exportfs', '-r'); 0284 if (system('PATH=/bin:/sbin pidof rpc.mountd >/dev/null') != 0 || 0285 system('PATH=/bin:/sbin pidof nfsd >/dev/null') != 0) { 0286 # trying to start the server... 0287 system('/etc/init.d/portmap start') if system('/etc/init.d/portmap status') != 0; 0288 system('/etc/init.d/nfs', $_) foreach 'stop', 'start'; 0289 } 0290 exit 0; 0291 } 0292 } 0293 0294 ################################################################################ 0295 package smb_exports; 0296 0297 use vars qw(@ISA $conf_file); 0298 BEGIN { @ISA = 'exports' } 0299 0300 sub read { 0301 my ($s, @l); 0302 local *F; 0303 open F, $conf_file; 0304 local $_; 0305 while (<F>) { 0306 if (/^\s*\[.*\]/ || eof F) { 0307 #- first line in the category 0308 my ($label) = $s =~ /^\s*\[(.*)\]/; 0309 my ($mntpoint) = $s =~ /^\s*path\s*=\s*(.*)/m; 0310 push @l, { mntpoint => $mntpoint, raw => $s, label => $label }; 0311 $s = ''; 0312 } 0313 $s .= $_; 0314 } 0315 bless \@l, 'smb_exports'; 0316 } 0317 0318 sub write { 0319 my ($smb_exports) = @_; 0320 foreach (@$smb_exports) { 0321 if (!exists $_->{raw}) { 0322 $_->{raw} = <<EOF; 0323 0324 [$_->{label}] 0325 path = $_->{mntpoint} 0326 comment = $_->{mntpoint} 0327 public = yes 0328 guest ok = yes 0329 writable = no 0330 wide links = no 0331 EOF 0332 } 0333 } 0334 local *F; 0335 open F, ">$conf_file" or die "can't write $conf_file"; 0336 print F $_->{raw} foreach @$smb_exports; 0337 } 0338 0339 sub add { 0340 my ($exports, $mntpoint) = @_; 0341 my $e = $exports->exports::add($mntpoint); 0342 $e->{label} = name_mangle($mntpoint, map { $_->{label} } @$exports); 0343 } 0344 0345 sub name_mangle { 0346 my ($input, @others) = @_; 0347 0348 local $_ = $input; 0349 0350 # 1. first only keep legal characters. "/" is also kept for the moment 0351 tr|a-z|A-Z|; 0352 s|[^A-Z0-9#\-_!/]|_|g; # "$" is allowed except at the end, remove it in any case 0353 0354 # 2. removing non-interesting parts 0355 s|^/||; 0356 s|^home/||; 0357 s|_*/_*|/|g; 0358 s|_+|_|g; 0359 0360 # 3. if size is too small (!), make it bigger 0361 $_ .= "_" while length($_) < 3; 0362 0363 # 4. if size is too big, shorten it 0364 while (length > 12) { 0365 my ($s) = m|.*?/(.*)|; 0366 if (length($s) > 8 && !grep { /\Q$s/ } @others) { 0367 # dropping leading directories when the resulting is still long and meaningful 0368 $_ = $s; 0369 next; 0370 } 0371 s|(.*)[0-9#\-_!/]|$1| and next; 0372 0373 # inspired by "Christian Brolin" "Long names are doom" on comp.lang.functional 0374 s|(.+)[AEIOU]|$1| and next; # allButFirstVowels 0375 s|(.*)(.)\2|$1$2| and next; # adjacentDuplicates 0376 0377 s|(.*).|$1|; # booh, :'-( 0378 } 0379 0380 # 5. remove "/"s still there 0381 s|/|_|g; 0382 0383 # 6. resolving conflicts 0384 my $l = join("|", map { quotemeta } @others); 0385 my $conflicts = qr|^($l)$|; 0386 if (/$conflicts/) { 0387 A: while (1) { 0388 for (my $nb = 1; length("$_$nb") <= 12; $nb++) { 0389 if ("$_$nb" !~ /$conflicts/) { 0390 $_ = "$_$nb"; 0391 last A; 0392 } 0393 } 0394 $_ or die "can't find a unique name"; 0395 # can't find a unique name, dropping the last letter 0396 s|(.*).|$1|; 0397 } 0398 } 0399 0400 # 7. done 0401 $_; 0402 } 0403 0404 sub update_server { 0405 if (fork) { 0406 system('/usr/bin/killall -HUP smbd 2>/dev/null'); 0407 if (system('PATH=/bin:/sbin pidof smbd >/dev/null') != 0 || 0408 system('PATH=/bin:/sbin pidof nmbd >/dev/null') != 0) { 0409 # trying to start the server... 0410 if ( -f '/etc/init.d/smb' ) { 0411 system('/etc/init.d/smb', $_) foreach 'stop', 'start'; 0412 } 0413 elsif ( -f '/etc/init.d/samba' ) { 0414 system('/etc/init.d/samba', $_) foreach 'stop', 'start'; 0415 } 0416 elsif ( -f '/etc/rc.d/rc.samba' ) { 0417 system('/etc/rc.d/rc.samba', $_) foreach 'stop', 'start'; 0418 } 0419 else { 0420 print STDERR "Error: Can't find the samba init script \n"; 0421 } 0422 } 0423 exit 0; 0424 } 0425 }