1 # Copyright © 2007-2011 Raphaël Hertzog <hertzog@debian.org>
2 # Copyright © 2011 Linaro Limited
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 Dpkg::Path - some common path handling functions
25 It provides some functions to handle various path.
29 package Dpkg
::Path
1.05;
37 check_files_are_the_same
38 check_directory_traversal
47 use Exporter
qw(import);
53 use Dpkg
::ErrorHandling
;
55 use Dpkg
::Arch
qw(get_host_arch debarch_to_debtuple);
62 =item get_pkg_root_dir($file)
64 This function will scan upwards the hierarchy of directory to find out
65 the directory which contains the "DEBIAN" sub-directory and it will return
66 its path. This directory is the root directory of a package being built.
68 If no DEBIAN subdirectory is found, it will return undef.
72 sub get_pkg_root_dir
($) {
75 $file =~ s{/+[^/]+$}{} if not -d
$file;
77 return $file if -d
"$file/DEBIAN";
78 last if $file !~ m{/};
79 $file =~ s{/+[^/]+$}{};
84 =item relative_to_pkg_root($file)
86 Returns the filename relative to get_pkg_root_dir($file).
90 sub relative_to_pkg_root
($) {
92 my $pkg_root = get_pkg_root_dir
($file);
93 if (defined $pkg_root) {
95 return $file if ($file =~ s/^\Q$pkg_root\E//);
100 =item guess_pkg_root_dir($file)
102 This function tries to guess the root directory of the package build tree.
103 It will first use get_pkg_root_dir(), but it will fallback to a more
104 imprecise check: namely it will use the parent directory that is a
105 sub-directory of the debian directory.
107 It can still return undef if a file outside of the debian sub-directory is
112 sub guess_pkg_root_dir
($) {
114 my $root = get_pkg_root_dir
($file);
115 return $root if defined $root;
118 $file =~ s{/+[^/]+$}{} if not -d
$file;
121 $parent =~ s{/+[^/]+$}{};
122 last if not -d
$parent;
123 return $file if check_files_are_the_same
('debian', $parent);
125 last if $file !~ m{/};
130 =item check_files_are_the_same($file1, $file2, $resolve_symlink)
132 This function verifies that both files are the same by checking that the device
133 numbers and the inode numbers returned by stat()/lstat() are the same. If
134 $resolve_symlink is true then stat() is used, otherwise lstat() is used.
138 sub check_files_are_the_same
($$;$) {
139 my ($file1, $file2, $resolve_symlink) = @_;
141 return 1 if $file1 eq $file2;
142 return 0 if ((! -e
$file1) || (! -e
$file2));
144 if ($resolve_symlink) {
145 @stat1 = stat($file1);
146 @stat2 = stat($file2);
148 @stat1 = lstat($file1);
149 @stat2 = lstat($file2);
151 my $result = ($stat1[0] == $stat2[0]) && ($stat1[1] == $stat2[1]);
156 =item canonpath($file)
158 This function returns a cleaned path. It simplifies double //, and remove
159 /./ and /../ intelligently. For /../ it simplifies the path only if the
160 previous element is not a symlink. Thus it should only be used on real
167 $path = File
::Spec
->canonpath($path);
168 my ($v, $dirs, $file) = File
::Spec
->splitpath($path);
169 my @dirs = File
::Spec
->splitdir($dirs);
171 foreach my $d (@dirs) {
173 if (scalar(@new) > 0 and $new[-1] ne '..') {
174 next if $new[-1] eq ''; # Root directory has no parent
175 my $parent = File
::Spec
->catpath($v,
176 File
::Spec
->catdir(@new), '');
177 if (not -l
$parent) {
189 return File
::Spec
->catpath($v, File
::Spec
->catdir(@new), $file);
192 =item $newpath = resolve_symlink($symlink)
194 Return the filename of the file pointed by the symlink. The new name is
195 canonicalized by canonpath().
199 sub resolve_symlink
($) {
201 my $content = readlink($symlink);
202 return unless defined $content;
203 if (File
::Spec
->file_name_is_absolute($content)) {
204 return canonpath
($content);
206 my ($link_v, $link_d, $link_f) = File
::Spec
->splitpath($symlink);
207 my ($cont_v, $cont_d, $cont_f) = File
::Spec
->splitpath($content);
208 my $new = File
::Spec
->catpath($link_v, $link_d . '/' . $cont_d, $cont_f);
209 return canonpath
($new);
213 =item check_directory_traversal($basedir, $dir)
215 This function verifies that the directory $dir does not contain any symlink
216 that goes beyond $basedir (which should be either equal or a parent of $dir).
220 sub check_directory_traversal
{
221 my ($basedir, $dir) = @_;
223 my $canon_basedir = realpath
($basedir);
224 # On Solaris /dev/null points to /devices/pseudo/mm@0:null.
225 my $canon_devnull = realpath
('/dev/null');
226 my $check_symlinks = sub {
227 my $canon_pathname = realpath
($_);
228 if (not defined $canon_pathname) {
229 return if $! == ENOENT
;
231 syserr
(g_
("pathname '%s' cannot be canonicalized"), $_);
233 return if $canon_pathname eq $canon_devnull;
234 return if $canon_pathname eq $canon_basedir;
235 return if $canon_pathname =~ m{^\Q$canon_basedir/\E};
237 error
(g_
("pathname '%s' points outside source root (to '%s')"),
238 $_, $canon_pathname);
242 wanted
=> $check_symlinks,
251 =item $cmdpath = find_command($command)
253 Return the path of the command if defined and available on an absolute or
254 relative path or on the $PATH, undef otherwise.
258 sub find_command
($) {
263 return "$cmd" if -x
"$cmd";
265 foreach my $dir (split(/:/, $ENV{PATH
})) {
266 return "$dir/$cmd" if -x
"$dir/$cmd";
272 =item $control_file = get_control_path($pkg, $filetype)
274 Return the path of the control file of type $filetype for the given
277 =item @control_files = get_control_path($pkg)
279 Return the path of all available control files for the given package.
283 sub get_control_path
($;$) {
284 my ($pkg, $filetype) = @_;
286 my @exec = ('dpkg-query', '--control-path', $pkg);
287 push @exec, $filetype if defined $filetype;
288 spawn
(exec => \
@exec, wait_child
=> 1, to_string
=> \
$control_file);
289 chomp($control_file);
290 if (defined $filetype) {
291 return if $control_file eq '';
292 return $control_file;
294 return () if $control_file eq '';
295 return split(/\n/, $control_file);
298 =item $file = find_build_file($basename)
300 Selects the right variant of the given file: the arch-specific variant
301 ("$basename.$arch") has priority over the OS-specific variant
302 ("$basename.$os") which has priority over the default variant
303 ("$basename"). If none of the files exists, then it returns undef.
305 =item @files = find_build_file($basename)
307 Return the available variants of the given file. Returns an empty
308 list if none of the files exists.
312 sub find_build_file
($) {
314 my $host_arch = get_host_arch
();
315 my ($abi, $libc, $host_os, $cpu) = debarch_to_debtuple
($host_arch);
317 foreach my $fn ("$base.$host_arch", "$base.$host_os", "$base") {
318 push @files, $fn if -f
$fn;
320 return @files if wantarray;
321 return $files[0] if scalar @files;
329 =head2 Version 1.05 (dpkg 1.20.4)
331 New function: check_directory_traversal().
333 =head2 Version 1.04 (dpkg 1.17.11)
335 Update semantics: find_command() now handles an empty or undef argument.
337 =head2 Version 1.03 (dpkg 1.16.1)
339 New function: find_build_file()
341 =head2 Version 1.02 (dpkg 1.16.0)
343 New function: get_control_path()
345 =head2 Version 1.01 (dpkg 1.15.8)
347 New function: find_command()
349 =head2 Version 1.00 (dpkg 1.15.6)
351 Mark the module as public.