File Coverage

blib/lib/Dpkg/Path.pm
Criterion Covered Total %
statement 108 134 80.6
branch 37 66 56.0
condition 5 9 55.5
subroutine 20 22 90.9
pod 10 10 100.0
total 180 241 74.6


line stmt bran cond sub pod time code
1             # Copyright © 2007-2011 Raphaël Hertzog
2             # Copyright © 2011 Linaro Limited
3             #
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.
8             #
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.
13             #
14             # You should have received a copy of the GNU General Public License
15             # along with this program. If not, see .
16              
17             package Dpkg::Path;
18              
19 522     522   256824 use strict;
  522         1082  
  522         15166  
20 522     522   2613 use warnings;
  522         1045  
  522         31419  
21              
22             our $VERSION = '1.05';
23             our @EXPORT_OK = qw(
24             canonpath
25             resolve_symlink
26             check_files_are_the_same
27             check_directory_traversal
28             find_command
29             find_build_file
30             get_control_path
31             get_pkg_root_dir
32             guess_pkg_root_dir
33             relative_to_pkg_root
34             );
35              
36 522     522   3166 use Exporter qw(import);
  522         1061  
  522         13086  
37 522     522   275628 use Errno qw(ENOENT);
  522         742093  
  522         47683  
38 522     522   3655 use File::Spec;
  522         1043  
  522         12508  
39 522     522   2606 use File::Find;
  522         1051  
  522         45269  
40 522     522   3652 use Cwd qw(realpath);
  522         1043  
  522         25092  
41              
42 522     522   4529 use Dpkg::ErrorHandling;
  522         531  
  522         44334  
43 522     522   3654 use Dpkg::Gettext;
  522         527  
  522         35464  
44 522     522   242452 use Dpkg::Arch qw(get_host_arch debarch_to_debtuple);
  522         1049  
  522         35555  
45 522     522   6225 use Dpkg::IPC;
  522         1048  
  522         766373  
46              
47             =encoding utf8
48              
49             =head1 NAME
50              
51             Dpkg::Path - some common path handling functions
52              
53             =head1 DESCRIPTION
54              
55             It provides some functions to handle various path.
56              
57             =head1 FUNCTIONS
58              
59             =over 8
60              
61             =item get_pkg_root_dir($file)
62              
63             This function will scan upwards the hierarchy of directory to find out
64             the directory which contains the "DEBIAN" sub-directory and it will return
65             its path. This directory is the root directory of a package being built.
66              
67             If no DEBIAN subdirectory is found, it will return undef.
68              
69             =cut
70              
71             sub get_pkg_root_dir($) {
72 6     6 1 689 my $file = shift;
73 6         27 $file =~ s{/+$}{};
74 6 50       106 $file =~ s{/+[^/]+$}{} if not -d $file;
75 6         20 while ($file) {
76 21 100       250 return $file if -d "$file/DEBIAN";
77 18 100       66 last if $file !~ m{/};
78 15         81 $file =~ s{/+[^/]+$}{};
79             }
80 3         11 return;
81             }
82              
83             =item relative_to_pkg_root($file)
84              
85             Returns the filename relative to get_pkg_root_dir($file).
86              
87             =cut
88              
89             sub relative_to_pkg_root($) {
90 2     2 1 6 my $file = shift;
91 2         5 my $pkg_root = get_pkg_root_dir($file);
92 2 100       10 if (defined $pkg_root) {
93 1         3 $pkg_root .= '/';
94 1 50       29 return $file if ($file =~ s/^\Q$pkg_root\E//);
95             }
96 1         6 return;
97             }
98              
99             =item guess_pkg_root_dir($file)
100              
101             This function tries to guess the root directory of the package build tree.
102             It will first use get_pkg_root_dir(), but it will fallback to a more
103             imprecise check: namely it will use the parent directory that is a
104             sub-directory of the debian directory.
105              
106             It can still return undef if a file outside of the debian sub-directory is
107             provided.
108              
109             =cut
110              
111             sub guess_pkg_root_dir($) {
112 2     2 1 5 my $file = shift;
113 2         5 my $root = get_pkg_root_dir($file);
114 2 100       14 return $root if defined $root;
115              
116 1         5 $file =~ s{/+$}{};
117 1 50       15 $file =~ s{/+[^/]+$}{} if not -d $file;
118 1         4 my $parent = $file;
119 1         5 while ($file) {
120 3         13 $parent =~ s{/+[^/]+$}{};
121 3 50       38 last if not -d $parent;
122 3 100       11 return $file if check_files_are_the_same('debian', $parent);
123 2         5 $file = $parent;
124 2 50       9 last if $file !~ m{/};
125             }
126 0         0 return;
127             }
128              
129             =item check_files_are_the_same($file1, $file2, $resolve_symlink)
130              
131             This function verifies that both files are the same by checking that the device
132             numbers and the inode numbers returned by stat()/lstat() are the same. If
133             $resolve_symlink is true then stat() is used, otherwise lstat() is used.
134              
135             =cut
136              
137             sub check_files_are_the_same($$;$) {
138 5     5 1 14 my ($file1, $file2, $resolve_symlink) = @_;
139 5 50 33     121 return 0 if ((! -e $file1) || (! -e $file2));
140 5         13 my (@stat1, @stat2);
141 5 50       11 if ($resolve_symlink) {
142 0         0 @stat1 = stat($file1);
143 0         0 @stat2 = stat($file2);
144             } else {
145 5         55 @stat1 = lstat($file1);
146 5         56 @stat2 = lstat($file2);
147             }
148 5   100     25 my $result = ($stat1[0] == $stat2[0]) && ($stat1[1] == $stat2[1]);
149 5         31 return $result;
150             }
151              
152              
153             =item canonpath($file)
154              
155             This function returns a cleaned path. It simplifies double //, and remove
156             /./ and /../ intelligently. For /../ it simplifies the path only if the
157             previous element is not a symlink. Thus it should only be used on real
158             filenames.
159              
160             =cut
161              
162             sub canonpath($) {
163 7     7 1 873 my $path = shift;
164 7         33 $path = File::Spec->canonpath($path);
165 7         83 my ($v, $dirs, $file) = File::Spec->splitpath($path);
166 7         40 my @dirs = File::Spec->splitdir($dirs);
167 7         12 my @new;
168 7         14 foreach my $d (@dirs) {
169 38 100       56 if ($d eq '..') {
170 5 50 33     26 if (scalar(@new) > 0 and $new[-1] ne '..') {
171 5 50       11 next if $new[-1] eq ''; # Root directory has no parent
172 5         47 my $parent = File::Spec->catpath($v,
173             File::Spec->catdir(@new), '');
174 5 100       83 if (not -l $parent) {
175 4         16 pop @new;
176             } else {
177 1         6 push @new, $d;
178             }
179             } else {
180 0         0 push @new, $d;
181             }
182             } else {
183 33         58 push @new, $d;
184             }
185             }
186 7         111 return File::Spec->catpath($v, File::Spec->catdir(@new), $file);
187             }
188              
189             =item $newpath = resolve_symlink($symlink)
190              
191             Return the filename of the file pointed by the symlink. The new name is
192             canonicalized by canonpath().
193              
194             =cut
195              
196             sub resolve_symlink($) {
197 3     3 1 7 my $symlink = shift;
198 3         41 my $content = readlink($symlink);
199 3 50       12 return unless defined $content;
200 3 100       26 if (File::Spec->file_name_is_absolute($content)) {
201 1         3 return canonpath($content);
202             } else {
203 2         23 my ($link_v, $link_d, $link_f) = File::Spec->splitpath($symlink);
204 2         15 my ($cont_v, $cont_d, $cont_f) = File::Spec->splitpath($content);
205 2         14 my $new = File::Spec->catpath($link_v, $link_d . '/' . $cont_d, $cont_f);
206 2         6 return canonpath($new);
207             }
208             }
209              
210             =item check_directory_traversal($basedir, $dir)
211              
212             This function verifies that the directory $dir does not contain any symlink
213             that goes beyond $basedir (which should be either equal or a parent of $dir).
214              
215             =cut
216              
217             sub check_directory_traversal {
218 17     17 1 41 my ($basedir, $dir) = @_;
219              
220 17         573 my $canon_basedir = realpath($basedir);
221             my $check_symlinks = sub {
222 84     84   5345 my $canon_pathname = realpath($_);
223 84 100       260 if (not defined $canon_pathname) {
224 1 50       11 return if $! == ENOENT;
225              
226 1         4 syserr(g_("pathname '%s' cannot be canonicalized"), $_);
227             }
228 83 100       175 return if $canon_pathname eq '/dev/null';
229 82 100       4930 return if $canon_pathname =~ m{^\Q$canon_basedir/\E};
230              
231 10         37 error(g_("pathname '%s' points outside source root (to '%s')"),
232             $_, $canon_pathname);
233 17         115 };
234              
235 17         1894 find({
236             wanted => $check_symlinks,
237             no_chdir => 1,
238             follow => 1,
239             follow_skip => 2,
240             }, $dir);
241              
242 6         49 return;
243             }
244              
245             =item $cmdpath = find_command($command)
246              
247             Return the path of the command if defined and available on an absolute or
248             relative path or on the $PATH, undef otherwise.
249              
250             =cut
251              
252             sub find_command($) {
253 520     520 1 506084 my $cmd = shift;
254              
255 520 50       2091 return if not $cmd;
256 520 50       2613 if ($cmd =~ m{/}) {
257 0 0       0 return "$cmd" if -x "$cmd";
258             } else {
259 520         3662 foreach my $dir (split(/:/, $ENV{PATH})) {
260 3120 100       69152 return "$dir/$cmd" if -x "$dir/$cmd";
261             }
262             }
263 0           return;
264             }
265              
266             =item $control_file = get_control_path($pkg, $filetype)
267              
268             Return the path of the control file of type $filetype for the given
269             package.
270              
271             =item @control_files = get_control_path($pkg)
272              
273             Return the path of all available control files for the given package.
274              
275             =cut
276              
277             sub get_control_path($;$) {
278 0     0 1   my ($pkg, $filetype) = @_;
279 0           my $control_file;
280 0           my @exec = ('dpkg-query', '--control-path', $pkg);
281 0 0         push @exec, $filetype if defined $filetype;
282 0           spawn(exec => \@exec, wait_child => 1, to_string => \$control_file);
283 0           chomp($control_file);
284 0 0         if (defined $filetype) {
285 0 0         return if $control_file eq '';
286 0           return $control_file;
287             }
288 0 0         return () if $control_file eq '';
289 0           return split(/\n/, $control_file);
290             }
291              
292             =item $file = find_build_file($basename)
293              
294             Selects the right variant of the given file: the arch-specific variant
295             ("$basename.$arch") has priority over the OS-specific variant
296             ("$basename.$os") which has priority over the default variant
297             ("$basename"). If none of the files exists, then it returns undef.
298              
299             =item @files = find_build_file($basename)
300              
301             Return the available variants of the given file. Returns an empty
302             list if none of the files exists.
303              
304             =cut
305              
306             sub find_build_file($) {
307 0     0 1   my $base = shift;
308 0           my $host_arch = get_host_arch();
309 0           my ($abi, $libc, $host_os, $cpu) = debarch_to_debtuple($host_arch);
310 0           my @files;
311 0           foreach my $f ("$base.$host_arch", "$base.$host_os", "$base") {
312 0 0         push @files, $f if -f $f;
313             }
314 0 0         return @files if wantarray;
315 0 0         return $files[0] if scalar @files;
316 0           return;
317             }
318              
319             =back
320              
321             =head1 CHANGES
322              
323             =head2 Version 1.05 (dpkg 1.20.4)
324              
325             New function: check_directory_traversal().
326              
327             =head2 Version 1.04 (dpkg 1.17.11)
328              
329             Update semantics: find_command() now handles an empty or undef argument.
330              
331             =head2 Version 1.03 (dpkg 1.16.1)
332              
333             New function: find_build_file()
334              
335             =head2 Version 1.02 (dpkg 1.16.0)
336              
337             New function: get_control_path()
338              
339             =head2 Version 1.01 (dpkg 1.15.8)
340              
341             New function: find_command()
342              
343             =head2 Version 1.00 (dpkg 1.15.6)
344              
345             Mark the module as public.
346              
347             =cut
348              
349             1;