Whamcloud - gitweb
LU-13397 lfs: mirror extend/copy keeps sparseness
[fs/lustre-release.git] / lustre / utils / liblustreapi_lseek.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * (C) Copyright (c) 2020, DataDirect Networks Inc, all rights reserved.
7  *
8  * All rights reserved. This program and the accompanying materials
9  * are made available under the terms of the GNU Lesser General Public License
10  * LGPL version 2.1 or (at your discretion) any later version.
11  * LGPL version 2.1 accompanies this distribution, and is available at
12  * http://www.gnu.org/licenses/lgpl-2.1.html
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * LGPL HEADER END
20  */
21 /*
22  * lustre/utils/liblustreapi_lseek.c
23  *
24  * lustreapi library for lseek-related functionality
25  *
26  * Author: Mikhail Pershin <mpershin@whamcloud.com>
27  */
28
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <stdbool.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <sys/syscall.h>
37 #include <sys/ioctl.h>
38 #include <errno.h>
39
40 #include <lustre/lustreapi.h>
41 #include "lustreapi_internal.h"
42
43 /**
44  * Check if file has a hole
45  *
46  * \param fd    file descriptor
47  *
48  * \retval boolean, true if file has a hole, false otherwise
49  */
50 bool llapi_file_is_sparse(int fd)
51 {
52         off_t file_end, hole_off;
53
54         file_end = lseek(fd, 0, SEEK_END);
55         hole_off = lseek(fd, 0, SEEK_HOLE);
56
57         /* Errors are ignored and file is just reported as non-sparse */
58         return file_end > 0 && hole_off >= 0 && hole_off < file_end;
59 }
60
61 /**
62  * Get the first data segment in given extent.
63  *
64  * \param src_fd  source file descriptor
65  * \param offset  offset to start from
66  * \param length  length of data segment found
67  *
68  * \retval next data offset and length on \p length on success.
69  * \retval -errno on failure.
70  */
71 off_t llapi_data_seek(int src_fd, off_t offset, size_t *length)
72 {
73         off_t data_off, hole_off;
74         int rc;
75
76         if (offset < 0) {
77                 rc = -EINVAL;
78                 llapi_error(LLAPI_MSG_ERROR, rc, "wrong offset: %jd",
79                             offset);
80                 return rc;
81         }
82
83         data_off = lseek(src_fd, offset, SEEK_DATA);
84         if (data_off < 0) {
85                 if (errno != ENXIO) {
86                         rc = -errno;
87                         llapi_error(LLAPI_MSG_ERROR, rc,
88                                     "failed SEEK_DATA from %jd",
89                                     offset);
90                         return rc;
91                 }
92                 hole_off = lseek(src_fd, 0, SEEK_END);
93                 if (data_off > hole_off) /* out of file range */
94                         return -ENXIO;
95                 /* no more data in src file, return end of file and zero size
96                  * so caller will know there must be hole up to that offset
97                  */
98                 *length = 0;
99                 return hole_off;
100         }
101
102         hole_off = lseek(src_fd, data_off, SEEK_HOLE);
103         if (hole_off < 0) {
104                 rc = -errno;
105                 llapi_error(LLAPI_MSG_ERROR, rc,
106                             "failed SEEK_HOLE from %jd", data_off);
107                 return rc;
108         }
109         *length = hole_off - data_off;
110         return data_off;
111 }