Whamcloud - gitweb
LU-9727 lustre: implement CL_OPEN for Changelogs
[fs/lustre-release.git] / lustre / utils / liblustreapi_chlg.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * (C) Copyright 2017 Commissariat a l'energie atomique et aux energies
7  *     alternatives
8  *
9  * All rights reserved. This program and the accompanying materials
10  * are made available under the terms of the GNU Lesser General Public License
11  * (LGPL) version 2.1 or (at your discretion) any later version.
12  * (LGPL) version 2.1 accompanies this distribution, and is available at
13  * http://www.gnu.org/licenses/lgpl-2.1.html
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * LGPL HEADER END
21  */
22 /*
23  * lustre/utils/liblustreapi_chlg.c
24  *
25  * lustreapi library for filesystem changelog
26  *
27  * Author: Henri Doreau <henri.doreau@cea.fr>
28  */
29
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <poll.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39
40 #include <lustre/lustreapi.h>
41
42
43 static int chlg_dev_path(char *path, size_t path_len, const char *device)
44 {
45         int rc;
46
47         rc = snprintf(path, path_len, "/dev/changelog-%s", device);
48         if (rc < 0)
49                 return -EIO;
50
51         if (rc >= path_len)
52                 return -EOVERFLOW;
53
54         return 0;
55 }
56
57 #define CHANGELOG_PRIV_MAGIC 0xCA8E1080
58 #define CHANGELOG_BUFFER_SZ  4096
59
60 /**
61  * Record state for efficient changelog consumption.
62  * Read chunks of CHANGELOG_BUFFER_SZ bytes.
63  */
64 struct changelog_private {
65         /* Ensure that the structure is valid and initialized */
66         int                              clp_magic;
67         /* File descriptor on the changelog character device */
68         int                              clp_fd;
69         /* Changelog delivery mode */
70         enum changelog_send_flag         clp_send_flags;
71         /* Changelog extra flags */
72         enum changelog_send_extra_flag   clp_send_extra_flags;
73         /* Available bytes in buffer */
74         size_t                           clp_buf_len;
75         /* Current position in buffer */
76         char                            *clp_buf_pos;
77         /* Read buffer with records read from system */
78         char                             clp_buf[0];
79 };
80
81 /**
82  * Start reading from a changelog
83  *
84  * @param priv      Opaque private control structure
85  * @param flags     Start flags (e.g. CHANGELOG_FLAG_JOBID)
86  * @param device    Report changes recorded on this MDT
87  * @param startrec  Report changes beginning with this record number
88  * (just call llapi_changelog_fini when done; don't need an endrec)
89  */
90 int llapi_changelog_start(void **priv, enum changelog_send_flag flags,
91                           const char *device, long long startrec)
92 {
93         struct changelog_private *cp;
94         static bool warned_extra_flags;
95         static bool warned_jobid;
96         static bool warned_follow;
97         char cdev_path[PATH_MAX];
98         int rc;
99
100         rc = chlg_dev_path(cdev_path, sizeof(cdev_path), device);
101         if (rc != 0)
102                 return rc;
103
104         /* Set up the receiver control struct */
105         cp = calloc(1, sizeof(*cp) + CHANGELOG_BUFFER_SZ);
106         if (cp == NULL)
107                 return -ENOMEM;
108
109         cp->clp_magic = CHANGELOG_PRIV_MAGIC;
110         cp->clp_send_flags = flags;
111
112         cp->clp_buf_len = 0;
113         cp->clp_buf_pos = cp->clp_buf;
114
115         /* Set up the receiver */
116         cp->clp_fd = open(cdev_path, O_RDONLY);
117         if (cp->clp_fd < 0) {
118                 rc = -errno;
119                 goto out_free_cp;
120         }
121
122         if (startrec != 0) {
123                 off_t res;
124
125                 res = lseek(cp->clp_fd, startrec, SEEK_SET);
126                 if (res == (off_t)-1) {
127                         rc = -errno;
128                         goto out_close;
129                 }
130         }
131
132         *priv = cp;
133
134         /* CHANGELOG_FLAG_EXTRA_FLAGS will eventually become mandatory.
135          * If it wasn't specified, display a warning here.
136          * Code elsewhere will remove the corresponding extension.
137          */
138         if (!(flags & CHANGELOG_FLAG_EXTRA_FLAGS) && !warned_extra_flags) {
139                 llapi_err_noerrno(LLAPI_MSG_WARN,
140                       "warning: %s() called without CHANGELOG_FLAG_EXTRA_FLAGS",
141                       __func__);
142                 warned_extra_flags = true;
143         }
144
145         /* CHANGELOG_FLAG_JOBID will eventually become mandatory. Display a
146          * warning if it's missing. */
147         if (!(flags & CHANGELOG_FLAG_JOBID) && !warned_jobid) {
148                 llapi_err_noerrno(LLAPI_MSG_WARN, "warning: %s() called "
149                                   "without CHANGELOG_FLAG_JOBID", __func__);
150                 warned_jobid = true;
151         }
152
153         /* Behavior expected by CHANGELOG_FLAG_FOLLOW is not implemented, warn
154          * the user and ignore it. */
155         if (flags & CHANGELOG_FLAG_FOLLOW && !warned_follow) {
156                 llapi_err_noerrno(LLAPI_MSG_WARN, "warning: %s() called with "
157                                   "CHANGELOG_FLAG_FOLLOW (ignored)", __func__);
158                 warned_follow = true;
159         }
160
161         return 0;
162
163 out_close:
164         close(cp->clp_fd);
165 out_free_cp:
166         free(cp);
167         return rc;
168 }
169
170 /** Finish reading from a changelog */
171 int llapi_changelog_fini(void **priv)
172 {
173         struct changelog_private *cp = *priv;
174
175         if (!cp || (cp->clp_magic != CHANGELOG_PRIV_MAGIC))
176                 return -EINVAL;
177
178         close(cp->clp_fd);
179         free(cp);
180         *priv = NULL;
181         return 0;
182 }
183
184 static ssize_t chlg_read_bulk(struct changelog_private *cp)
185 {
186         ssize_t rd_bytes;
187
188         if (!cp || cp->clp_magic != CHANGELOG_PRIV_MAGIC)
189                 return -EINVAL;
190
191         rd_bytes = read(cp->clp_fd, cp->clp_buf, CHANGELOG_BUFFER_SZ);
192         if (rd_bytes < 0)
193                 return -errno;
194
195         cp->clp_buf_pos = cp->clp_buf;
196         cp->clp_buf_len = rd_bytes;
197
198         return rd_bytes;
199 }
200
201 /**
202  * Returns a file descriptor to poll on.
203  *
204  * \@param[in]  priv  Opaque changelog reader structure.
205  * @return valid file descriptor on success, negated errno code on failure.
206  */
207 int llapi_changelog_get_fd(void *priv)
208 {
209         struct changelog_private *cp = priv;
210
211         if (!cp || cp->clp_magic != CHANGELOG_PRIV_MAGIC)
212                 return -EINVAL;
213
214         return cp->clp_fd;
215 }
216
217 /** Read the next changelog entry
218  * @param priv Opaque private control structure
219  * @param rech Changelog record handle; record will be allocated here
220  * @return 0 valid message received; rec is set
221  *       <0 error code
222  *       1 EOF
223  */
224 #define DEFAULT_RECORD_FMT      (CLF_VERSION | CLF_RENAME)
225 int llapi_changelog_recv(void *priv, struct changelog_rec **rech)
226 {
227         struct changelog_private *cp = priv;
228         enum changelog_rec_flags rec_fmt = DEFAULT_RECORD_FMT;
229         enum changelog_rec_extra_flags rec_extra_fmt = CLFE_INVALID;
230         struct changelog_rec *tmp;
231         int rc = 0;
232
233         if (!cp || (cp->clp_magic != CHANGELOG_PRIV_MAGIC))
234                 return -EINVAL;
235
236         if (rech == NULL)
237                 return -EINVAL;
238
239         *rech = malloc(CR_MAXSIZE);
240         if (*rech == NULL)
241                 return -ENOMEM;
242
243         if (cp->clp_send_flags & CHANGELOG_FLAG_JOBID)
244                 rec_fmt |= CLF_JOBID;
245
246         if (cp->clp_send_flags & CHANGELOG_FLAG_EXTRA_FLAGS) {
247                 rec_fmt |= CLF_EXTRA_FLAGS;
248                 if (cp->clp_send_extra_flags & CHANGELOG_EXTRA_FLAG_UIDGID)
249                         rec_extra_fmt |= CLFE_UIDGID;
250                 if (cp->clp_send_extra_flags & CHANGELOG_EXTRA_FLAG_NID)
251                         rec_extra_fmt |= CLFE_NID;
252                 if (cp->clp_send_extra_flags & CHANGELOG_EXTRA_FLAG_OMODE)
253                         rec_extra_fmt |= CLFE_OPEN;
254         }
255
256         if (cp->clp_buf + cp->clp_buf_len <= cp->clp_buf_pos) {
257                 ssize_t refresh;
258
259                 refresh = chlg_read_bulk(cp);
260                 if (refresh == 0) {
261                         /* EOF, CHANGELOG_FLAG_FOLLOW ignored for now LU-7659 */
262                         rc = 1;
263                         goto out_free;
264                 } else if (refresh < 0) {
265                         rc = refresh;
266                         goto out_free;
267                 }
268         }
269
270         /* TODO check changelog_rec_size */
271         tmp = (struct changelog_rec *)cp->clp_buf_pos;
272
273         memcpy(*rech, cp->clp_buf_pos,
274                changelog_rec_size(tmp) + tmp->cr_namelen);
275
276         cp->clp_buf_pos += changelog_rec_size(tmp) + tmp->cr_namelen;
277         changelog_remap_rec(*rech, rec_fmt, rec_extra_fmt);
278
279         return 0;
280
281 out_free:
282         free(*rech);
283         *rech = NULL;
284         return rc;
285 }
286
287 /** Release the changelog record when done with it. */
288 int llapi_changelog_free(struct changelog_rec **rech)
289 {
290         free(*rech);
291         *rech = NULL;
292         return 0;
293 }
294
295 int llapi_changelog_clear(const char *mdtname, const char *idstr,
296                           long long endrec)
297 {
298         char dev_path[PATH_MAX];
299         char cmd[64];
300         size_t cmd_len = sizeof(cmd);
301         int fd;
302         int rc;
303
304         if (endrec < 0) {
305                 llapi_err_noerrno(LLAPI_MSG_ERROR,
306                                   "can't purge negative records\n");
307                 return -EINVAL;
308         }
309
310         chlg_dev_path(dev_path, sizeof(dev_path), mdtname);
311
312         rc = snprintf(cmd, cmd_len, "clear:%s:%lld", idstr, endrec);
313         if (rc >= sizeof(cmd))
314                 return -EINVAL;
315
316         cmd_len = rc + 1;
317
318         fd = open(dev_path, O_WRONLY);
319         if (fd < 0) {
320                 rc = -errno;
321                 llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'", dev_path);
322                 return rc;
323         }
324
325         rc = write(fd, cmd, cmd_len);
326         if (rc < 0) {
327                 rc = -errno;
328                 llapi_error(LLAPI_MSG_ERROR, rc,
329                             "cannot purge records for '%s'", idstr);
330                 goto out_close;
331         }
332
333         rc = 0;
334
335 out_close:
336         close(fd);
337         return rc;
338 }
339
340 /**
341  * Set extra flags for reading changelogs
342  *
343  * @param priv          Opaque private control structure
344  * @param extra_flags   Read extra flags (e.g. CHANGELOG_EXTRA_FLAG_UIDGID)
345  *
346  * Just call this function right after llapi_changelog_start().
347  */
348 int llapi_changelog_set_xflags(void *priv,
349                                enum changelog_send_extra_flag extra_flags)
350 {
351         struct changelog_private *cp = priv;
352         static bool warned_uidgid;
353
354         if (!cp || cp->clp_magic != CHANGELOG_PRIV_MAGIC)
355                 return -EINVAL;
356
357         cp->clp_send_extra_flags = extra_flags;
358
359         /* CHANGELOG_EXTRA_FLAG_UIDGID will eventually become mandatory.
360          * If it wasn't specified, display a warning here.
361          * Code elsewhere will remove the corresponding extension.
362          */
363         if (!(extra_flags & CHANGELOG_EXTRA_FLAG_UIDGID) && !warned_uidgid) {
364                 llapi_err_noerrno(LLAPI_MSG_WARN,
365                      "warning: %s() called without CHANGELOG_EXTRA_FLAG_UIDGID",
366                      __func__);
367                 warned_uidgid = true;
368         }
369
370         return 0;
371 }