tests: more automated quote adjustment
[coreutils/ericb.git] / src / extent-scan.c
blobe269f54aaeaf185403f881fe72d4227c459a733e
1 /* extent-scan.c -- core functions for scanning extents
2 Copyright (C) 2010-2012 Free Software Foundation, Inc.
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 3 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 <http://www.gnu.org/licenses/>.
17 Written by Jie Liu (jeff.liu@oracle.com). */
19 #include <config.h>
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #include <sys/utsname.h>
23 #include <assert.h>
25 #include "system.h"
26 #include "extent-scan.h"
27 #include "fiemap.h"
28 #include "xstrtol.h"
31 /* Work around Linux kernel issues on BTRFS and EXT4 before 2.6.39.
32 FIXME: remove in 2013, or whenever we're pretty confident
33 that the offending, unpatched kernels are no longer in use. */
34 static bool
35 extent_need_sync (void)
37 /* For now always return true, to be on the safe side.
38 If/when FIEMAP semantics are well defined (before SEEK_HOLE support
39 is usable) and kernels implementing them are in use, we may relax
40 this once again. */
41 return true;
43 #if FIEMAP_BEHAVIOR_IS_DEFINED_AND_USABLE
44 static int need_sync = -1;
46 if (need_sync == -1)
48 struct utsname name;
49 need_sync = 0; /* No workaround by default. */
51 # ifdef __linux__
52 if (uname (&name) != -1 && STRNCMP_LIT (name.release, "2.6.") == 0)
54 unsigned long val;
55 if (xstrtoul (name.release + 4, NULL, 10, &val, NULL) == LONGINT_OK)
57 if (val < 39)
58 need_sync = 1;
61 # endif
64 return need_sync;
65 #endif
68 /* Allocate space for struct extent_scan, initialize the entries if
69 necessary and return it as the input argument of extent_scan_read(). */
70 extern void
71 extent_scan_init (int src_fd, struct extent_scan *scan)
73 scan->fd = src_fd;
74 scan->ei_count = 0;
75 scan->ext_info = NULL;
76 scan->scan_start = 0;
77 scan->initial_scan_failed = false;
78 scan->hit_final_extent = false;
79 scan->fm_flags = extent_need_sync () ? FIEMAP_FLAG_SYNC : 0;
82 #ifdef __linux__
83 # ifndef FS_IOC_FIEMAP
84 # define FS_IOC_FIEMAP _IOWR ('f', 11, struct fiemap)
85 # endif
86 /* Call ioctl(2) with FS_IOC_FIEMAP (available in linux 2.6.27) to
87 obtain a map of file extents excluding holes. */
88 extern bool
89 extent_scan_read (struct extent_scan *scan)
91 unsigned int si = 0;
92 struct extent_info *last_ei IF_LINT ( = scan->ext_info);
94 while (true)
96 union { struct fiemap f; char c[4096]; } fiemap_buf;
97 struct fiemap *fiemap = &fiemap_buf.f;
98 struct fiemap_extent *fm_extents = &fiemap->fm_extents[0];
99 enum { count = (sizeof fiemap_buf - sizeof *fiemap)/sizeof *fm_extents };
100 verify (count > 1);
102 /* This is required at least to initialize fiemap->fm_start,
103 but also serves (in mid 2010) to appease valgrind, which
104 appears not to know the semantics of the FIEMAP ioctl. */
105 memset (&fiemap_buf, 0, sizeof fiemap_buf);
107 fiemap->fm_start = scan->scan_start;
108 fiemap->fm_flags = scan->fm_flags;
109 fiemap->fm_extent_count = count;
110 fiemap->fm_length = FIEMAP_MAX_OFFSET - scan->scan_start;
112 /* Fall back to the standard copy if call ioctl(2) failed for
113 the first time. */
114 if (ioctl (scan->fd, FS_IOC_FIEMAP, fiemap) < 0)
116 if (scan->scan_start == 0)
117 scan->initial_scan_failed = true;
118 return false;
121 /* If 0 extents are returned, then no more scans are needed. */
122 if (fiemap->fm_mapped_extents == 0)
124 scan->hit_final_extent = true;
125 return scan->scan_start != 0;
128 assert (scan->ei_count <= SIZE_MAX - fiemap->fm_mapped_extents);
129 scan->ei_count += fiemap->fm_mapped_extents;
130 scan->ext_info = xnrealloc (scan->ext_info, scan->ei_count,
131 sizeof (struct extent_info));
133 unsigned int i = 0;
134 for (i = 0; i < fiemap->fm_mapped_extents; i++)
136 assert (fm_extents[i].fe_logical <=
137 OFF_T_MAX - fm_extents[i].fe_length);
139 if (si && last_ei->ext_flags ==
140 (fm_extents[i].fe_flags & ~FIEMAP_EXTENT_LAST)
141 && (last_ei->ext_logical + last_ei->ext_length
142 == fm_extents[i].fe_logical))
144 /* Merge previous with last. */
145 last_ei->ext_length += fm_extents[i].fe_length;
146 /* Copy flags in case different. */
147 last_ei->ext_flags = fm_extents[i].fe_flags;
149 else if ((si == 0 && scan->scan_start > fm_extents[i].fe_logical)
150 || (si && last_ei->ext_logical + last_ei->ext_length >
151 fm_extents[i].fe_logical))
153 /* BTRFS before 2.6.38 could return overlapping extents
154 for sparse files. We adjust the returned extents
155 rather than failing, as otherwise it would be inefficient
156 to detect this on the initial scan. */
157 uint64_t new_logical;
158 uint64_t length_adjust;
159 if (si == 0)
160 new_logical = scan->scan_start;
161 else
163 /* We could return here if scan->scan_start == 0
164 but don't so as to minimize special cases. */
165 new_logical = last_ei->ext_logical + last_ei->ext_length;
167 length_adjust = new_logical - fm_extents[i].fe_logical;
168 /* If an extent is contained within the previous one, fail. */
169 if (length_adjust < fm_extents[i].fe_length)
171 if (scan->scan_start == 0)
172 scan->initial_scan_failed = true;
173 return false;
175 fm_extents[i].fe_logical = new_logical;
176 fm_extents[i].fe_length -= length_adjust;
177 /* Process the adjusted extent again. */
178 i--;
179 continue;
181 else
183 last_ei = scan->ext_info + si;
184 last_ei->ext_logical = fm_extents[i].fe_logical;
185 last_ei->ext_length = fm_extents[i].fe_length;
186 last_ei->ext_flags = fm_extents[i].fe_flags;
187 si++;
191 if (last_ei->ext_flags & FIEMAP_EXTENT_LAST)
192 scan->hit_final_extent = true;
194 /* If we have enough extents, discard the last as it might
195 be merged with one from the next scan. */
196 if (si > count && !scan->hit_final_extent)
197 last_ei = scan->ext_info + --si - 1;
199 /* We don't bother reallocating any trailing slots. */
200 scan->ei_count = si;
202 if (scan->hit_final_extent)
203 break;
204 else
205 scan->scan_start = last_ei->ext_logical + last_ei->ext_length;
207 if (si >= count)
208 break;
211 return true;
213 #else
214 extern bool
215 extent_scan_read (struct extent_scan *scan ATTRIBUTE_UNUSED)
217 scan->initial_scan_failed = true;
218 errno = ENOTSUP;
219 return false;
221 #endif