1 /* 2 * Copyright (c) 2016 Jeff Mahoney <jeffm (at) suse.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "defs.h" 29 #include <linux/ioctl.h> 30 #include <linux/fs.h> 31 32 #ifdef HAVE_LINUX_FIEMAP_H 33 # include <linux/fiemap.h> 34 # include "xlat/fiemap_flags.h" 35 # include "xlat/fiemap_extent_flags.h" 36 #endif 37 38 #ifndef FICLONE 39 # define FICLONE _IOW(0x94, 9, int) 40 #endif 41 42 #ifndef FICLONERANGE 43 # define FICLONERANGE _IOW(0x94, 13, struct file_clone_range) 44 struct file_clone_range { 45 int64_t src_fd; 46 uint64_t src_offset; 47 uint64_t src_length; 48 uint64_t dest_offset; 49 }; 50 #endif 51 52 #ifndef FIDEDUPERANGE 53 # define FIDEDUPERANGE _IOWR(0x94, 54, struct file_dedupe_range) 54 struct file_dedupe_range_info { 55 int64_t dest_fd; /* in - destination file */ 56 uint64_t dest_offset; /* in - start of extent in destination */ 57 uint64_t bytes_deduped; /* out - total # of bytes we were able 58 * to dedupe from this file. */ 59 /* status of this dedupe operation: 60 * < 0 for error 61 * == FILE_DEDUPE_RANGE_SAME if dedupe succeeds 62 * == FILE_DEDUPE_RANGE_DIFFERS if data differs 63 */ 64 int32_t status; /* out - see above description */ 65 uint32_t reserved; /* must be zero */ 66 }; 67 68 struct file_dedupe_range { 69 uint64_t src_offset; /* in - start of extent in source */ 70 uint64_t src_length; /* in - length of extent */ 71 uint16_t dest_count; /* in - total elements in info array */ 72 uint16_t reserved1; /* must be zero */ 73 uint32_t reserved2; /* must be zero */ 74 struct file_dedupe_range_info info[0]; 75 }; 76 #endif 77 78 static bool 79 print_file_dedupe_range_info(struct tcb *tcp, void *elem_buf, 80 size_t elem_size, void *data) 81 { 82 const struct file_dedupe_range_info *info = elem_buf; 83 unsigned int *count = data; 84 85 if (count) { 86 if (*count == 0) { 87 tprints("..."); 88 return false; 89 } 90 --*count; 91 } 92 93 if (entering(tcp)) { 94 tprints("{dest_fd="); 95 printfd(tcp, info->dest_fd); 96 tprintf(", dest_offset=%" PRIu64 "}", 97 (uint64_t) info->dest_offset); 98 } else { 99 tprintf("{bytes_deduped=%" PRIu64 ", status=%d}", 100 (uint64_t) info->bytes_deduped, info->status); 101 } 102 103 return true; 104 } 105 106 #ifdef HAVE_LINUX_FIEMAP_H 107 static bool 108 print_fiemap_extent(struct tcb *tcp, void *elem_buf, size_t elem_size, void *data) 109 { 110 const struct fiemap_extent *fe = elem_buf; 111 112 tprintf("{fe_logical=%" PRI__u64 113 ", fe_physical=%" PRI__u64 114 ", fe_length=%" PRI__u64 ", ", 115 fe->fe_logical, fe->fe_physical, fe->fe_length); 116 117 printflags64(fiemap_extent_flags, fe->fe_flags, 118 "FIEMAP_EXTENT_???"); 119 tprints("}"); 120 121 return true; 122 } 123 #endif /* HAVE_LINUX_FIEMAP_H */ 124 125 int 126 file_ioctl(struct tcb *const tcp, const unsigned int code, 127 const kernel_ulong_t arg) 128 { 129 switch (code) { 130 case FICLONE: /* W */ 131 tprintf(", %d", (int) arg); 132 break; 133 134 case FICLONERANGE: { /* W */ 135 struct file_clone_range args; 136 137 tprints(", "); 138 if (umove_or_printaddr(tcp, arg, &args)) 139 break; 140 141 tprints("{src_fd="); 142 printfd(tcp, args.src_fd); 143 tprintf(", src_offset=%" PRIu64 144 ", src_length=%" PRIu64 145 ", dest_offset=%" PRIu64 "}", 146 (uint64_t) args.src_offset, 147 (uint64_t) args.src_length, 148 (uint64_t) args.dest_offset); 149 break; 150 } 151 152 case FIDEDUPERANGE: { /* RW */ 153 struct file_dedupe_range args; 154 struct file_dedupe_range_info info; 155 unsigned int *limit = NULL; 156 unsigned int count = 2; 157 bool rc; 158 159 if (entering(tcp)) 160 tprints(", "); 161 else if (syserror(tcp)) 162 break; 163 else 164 tprints(" => "); 165 166 if (umove_or_printaddr(tcp, arg, &args)) 167 break; 168 169 tprints("{"); 170 if (entering(tcp)) { 171 tprintf("src_offset=%" PRIu64 172 ", src_length=%" PRIu64 173 ", dest_count=%hu, ", 174 (uint64_t) args.src_offset, 175 (uint64_t) args.src_length, 176 (uint16_t) args.dest_count); 177 } 178 179 tprints("info="); 180 181 /* Limit how many elements we print in abbrev mode. */ 182 if (abbrev(tcp) && args.dest_count > count) 183 limit = &count; 184 185 rc = print_array(tcp, arg + offsetof(typeof(args), info), 186 args.dest_count, &info, sizeof(info), 187 umoven_or_printaddr, 188 print_file_dedupe_range_info, limit); 189 190 tprints("}"); 191 if (!rc || exiting(tcp)) 192 break; 193 194 return 0; 195 } 196 197 #ifdef HAVE_LINUX_FIEMAP_H 198 case FS_IOC_FIEMAP: { 199 struct fiemap args; 200 201 if (entering(tcp)) 202 tprints(", "); 203 else if (syserror(tcp)) 204 break; 205 else 206 tprints(" => "); 207 208 if (umove_or_printaddr(tcp, arg, &args)) 209 break; 210 211 if (entering(tcp)) { 212 tprintf("{fm_start=%" PRI__u64 ", " 213 "fm_length=%" PRI__u64 ", " 214 "fm_flags=", 215 args.fm_start, args.fm_length); 216 printflags64(fiemap_flags, args.fm_flags, 217 "FIEMAP_FLAG_???"); 218 tprintf(", fm_extent_count=%u}", args.fm_extent_count); 219 return 0; 220 } 221 222 tprints("{fm_flags="); 223 printflags64(fiemap_flags, args.fm_flags, 224 "FIEMAP_FLAG_???"); 225 tprintf(", fm_mapped_extents=%u", 226 args.fm_mapped_extents); 227 tprints(", fm_extents="); 228 if (abbrev(tcp)) { 229 tprints("..."); 230 } else { 231 struct fiemap_extent fe; 232 print_array(tcp, 233 arg + offsetof(typeof(args), fm_extents), 234 args.fm_mapped_extents, &fe, sizeof(fe), 235 umoven_or_printaddr, 236 print_fiemap_extent, 0); 237 } 238 tprints("}"); 239 240 break; 241 } 242 #endif /* HAVE_LINUX_FIEMAP_H */ 243 244 default: 245 return RVAL_DECODED; 246 }; 247 248 return RVAL_DECODED | 1; 249 } 250