e49990489b8ddde4eaf16d5be08c5ce45f969695
[lufs.git] / kernel / Linux / prepmod.in
1 #! /usr/bin/perl -w
2
3 # $Id$
4 # Check and possibly rebuild lufs kernel module for the current kernel.
5 # Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; exactly version 2 of June 1991 is required
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
21 use strict;
22 use Carp qw(cluck confess);
23 use Getopt::Long;
24 # Do not use any non-core Perl modules here.
25 # Be perl-5.0 compatible (is it needed if we require Linux kernel >= 2.4 ?)
26
27
28 my $basedir='@datadir@/lufs';
29 my $vardir='@localstatedir@/lib/lufs';
30 $basedir=~s#\$\Q{prefix}\E#'@prefix@';#ge;
31 $vardir=~s#\$\Q{prefix}\E#'@prefix@';#ge;
32 sub srcdir { my($uname_r)=@_; $basedir."/".($uname_r lt "2.5" ? "2.4" : "2.5"); }
33 my $modbindir=$basedir."/modbin";
34 my @sources=qw(proc.c inode.c dir.c file.c symlink.c);
35 my $tmp_dir="prepmod-tmp-dir";
36
37 my $quiet;
38 my $kernel;
39 my $prebuild;
40 my $kernel_gcc_args="";
41
42 my $lufsmnt_bin;
43 if ($0 eq "lufsmnt" || $0=~m#/lufsmnt$#) {
44         $quiet=1;
45         $lufsmnt_bin='@bindir@/lufsmnt-bin';
46         }
47 else {
48         die if !GetOptions(
49                         "q|quiet",\$quiet,
50                           "kernel",\$kernel,    # it must contain "include" subdir
51                           "modbindir=s",\$modbindir,
52                           "basedir=s",\$basedir,
53                           "prebuild",\&prebuild,
54                           "kernel-gcc-args=s",\$kernel_gcc_args,
55                         );
56         }
57
58
59 sub _readfile
60 {
61 my($filename,$optional)=@_;
62
63         local $/=undef();
64         local *F;
65         open F,$filename or do { cluck "Open \"$filename\": $!" if !$optional; return ""; };
66         my $r=<F>;
67         close F or cluck "Close \"$filename\": $!";
68         return $r;
69 }
70
71 sub _system
72 {
73 my(@args)=@_;
74
75         print STDERR "+ ".join(" ",@args)."\n" if !$quiet;
76         return system @args;
77 }
78
79 sub _pass
80 {
81 my($load)=@_;
82
83         my $modproberc;
84         if ($load) {
85                 do { ($modproberc=_system $_) and cluck "$_ failed - ignoring"; } for ("depmod -aq","modprobe lufs");
86                 }
87         exit ($modproberc ? 1 : 0) if !$lufsmnt_bin;
88         do { exec $lufsmnt_bin,@ARGV; };
89         confess "Failed to exec '$lufsmnt_bin': $!";
90 }
91
92
93 my $filesystems=_readfile "/proc/filesystems";  # 'lufs' may be built-in
94 _pass if $filesystems=~/\blufs$/m;
95 my $modules=_readfile "/proc/modules";  # 'lufs' may be already loaded
96 _pass if $modules=~/^lufs\b/m;
97 _pass if !_system "modprobe lufs 2>/dev/null";
98
99 my $proc_version=_readfile "/proc/version";
100 my $uname_r=($proc_version=~/^Linux version (\S+)/)[0] || _readfile "uname -r|";
101 chomp $uname_r;
102 confess "Failed to detect kernel version" if !$uname_r;
103 my $uname_r_base=($uname_r=~/^([^-]+)/)[0];
104 print STDERR "Running kernel version: $uname_r (base version $uname_r_base)\n" if !$quiet;
105
106 do { $kernel||=$_ if -d $_; } for ("/lib/modules/$uname_r/build");
107 do { $kernel||=$_ if -d $_; } for ("/usr/src/kernel-headers-$uname_r");
108 do { $kernel||=$_ if -d $_; } for ("/usr/src/linux-$uname_r");
109 do { $kernel||=$_ if -d $_; } for ("/usr/src/linux-$uname_r_base");
110 do { $kernel||=$_ if -d $_; } for ("/usr/src/linux");
111 confess "Failed to find kernel headers for $uname_r" if !$kernel;
112
113 my $moduledir="/lib/modules/$uname_r/kernel/fs/lufs";
114 print STDERR "Destination module directory: $moduledir\n" if !$quiet;
115
116 for ("$vardir/lufs.o") {
117         # Create the 'lufs.o' in our /var/lib directory and only link it
118         # to prevent using obsolete modules after upgrading 'lufs' package.
119         # depmod(1) will take the larget symlink name - we must create directory for  it.
120         if (build($kernel,$uname_r,$_)) {
121                 do { cluck "Failed to symlink $_"; next; }
122                                 if _system "rm -rf $moduledir; mkdir -p $moduledir; ln -s $_ $moduledir/lufs.o";
123                 _pass 1;
124                 }
125         }
126
127 local $_;
128 while (<$modbindir/*.o>) {
129         next if _system "rmmod lufs 2>/dev/null; insmod -o lufs -p $_ 2>/dev/null";
130         do { cluck "Failed to symlink $_"; next; } if _system "mkdir -p $moduledir; ln -s $_ $moduledir/lufs.o";
131         _pass 1;
132         }
133 confess "lufs module not loaded: $basedir not compilable and no module of $modbindir/*.o usable";
134
135
136 sub build
137 {
138 my($kernel,$uname_r,$destmodule)=@_;
139
140         print STDERR "Using kernel sources: $kernel\n" if !$quiet;
141         confess "Kernel sources $kernel do not contain 'include' subdirectory" if ! -d $kernel."/include";
142
143         my $kdebug="";
144         do { $kdebug=$_ if !/^@/; } for ('@KDEBUG_FLAGS@');
145         my $cmdline="gcc"
146                         .($quiet ? " 2>/dev/null" : "")
147                         ." -O2 -pipe -fomit-frame-pointer -fno-strict-aliasing -mpreferred-stack-boundary=2 -Wall"
148                         ." -D__KERNEL__ -DMODULE -DLINUX -DKBUILD_MODNAME=lufs"
149                         ." ".$kdebug
150                         ." ".$kernel_gcc_args
151                         ." -I$kernel/include"
152                         ." -I$kernel/include/asm-i386/mach-default";    # gcc should not care if this 2.5 dir does not exist
153
154         my $config=_readfile "$kernel/.config","optional";
155         my $autoconf=_readfile "$kernel/include/linux/autoconf.h","optional";
156         $cmdline.=" -DMODVERSIONS -include $kernel/include/linux/modversions.h"
157                         if 0
158                                         || $config=~/^CONFIG_MODVERSIONS=y\b/m
159                                         || $autoconf=~/^#define CONFIG_MODVERSIONS\b/m
160                                         || (!$config && !$autoconf);    # assume modversions if not known
161
162         my @objects=map({ my $o=$_; $o=~s/[.]c$/.o/; $o; } @sources);
163         return !_system "set -e; mkdir -p `dirname $destmodule`; rm -f $destmodule;"
164                         .(!$vardir ? "" : " cd $vardir;")
165                         ." $cmdline -c ".join(" ",map({ srcdir($uname_r)."/".$_; } @sources)).";"
166                         ." ld -r -o $destmodule ".join(" ",@objects).";"
167                         ." rm -f ".join(" ",@objects);
168 }
169
170
171 sub prebuild_kernel
172 {
173 my($dir,$vendor,$uname_r)=@_;
174
175         confess "Unrecognized vendor for dir: $dir" if !$vendor;
176         confess "Unrecognized uname_r for dir: $dir" if !$uname_r;
177         confess "Failed to build $dir" if !build $dir,$uname_r,$modbindir."/lufs-$vendor-$uname_r.o";
178 }
179
180
181 sub prebuild_rpm
182 {
183 my($rpm)=@_;
184
185         my $vendor=_readfile "rpm 2>/dev/null --qf '%{VENDOR}' -qp '$rpm' |";
186         $vendor=~s/,.*//;
187         $vendor=~tr/ //d;
188         my $uname_r;
189         $uname_r||=($rpm=~m#/kernel-source-([^-]+-[^-]+)[.][^.]+[.]rpm$#)[0];
190         _system "rm -rf $tmp_dir; mkdir $tmp_dir; set -e;"
191                         # Here we will extract even some false 'include's but we filter dirs just for speed/space.
192                         ." rpm2cpio '$rpm' | (cd $tmp_dir; cpio -id --quiet ./usr/src/linux*/{.config,include/*})"
193                         and confess "Extraction of: $rpm";
194         prebuild_kernel $tmp_dir."/usr/src/linux-$uname_r",$vendor,$uname_r;
195         _system "rm -rf $tmp_dir" and confess "Deletion of: $tmp_dir";
196 }
197
198 sub prebuild_dir
199 {
200 my($dir)=@_;
201
202         my $vendor;
203         chomp (my $debian=_readfile "/etc/debian_version","optional");
204         $vendor="Debian".$debian if $debian && $dir=~m#^/usr/src/#;
205         my $uname_r;
206         $uname_r||=($dir=~m#/kernel-headers-([^/]+)/*$#)[0];
207         $uname_r||=($dir=~m#/linux-([^/]+)/*$#)[0];
208         $uname_r||=($dir=~m#^/lib/modules/([^/]+)/build/*$#)[0];
209         prebuild_kernel $dir,$vendor,$uname_r;
210 }
211
212 sub prebuild
213 {
214         $vardir=undef();        # we may not yet have /var/lib/lufs
215         for (@ARGV) {
216                 do { prebuild_rpm $_; next; } if /[.]rpm$/;
217                 do { prebuild_dir $_; next; } if -d $_;
218                 confess "Unrecognized kernel: $_";
219                 }
220         exit 0;
221 }