Whamcloud - gitweb
LU-13055 mdd: per-user changelog names and mask
[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 #include <sys/ioctl.h>
40
41 #include <lustre/lustreapi.h>
42 #include <linux/lustre/lustre_ioctl.h>
43
44
45 static int chlg_dev_path(char *path, size_t path_len, const char *device)
46 {
47         int rc;
48
49         rc = snprintf(path, path_len, "/dev/changelog-%s", device);
50         if (rc < 0)
51                 return -EIO;
52
53         if (rc >= path_len)
54                 return -EOVERFLOW;
55
56         return 0;
57 }
58
59 #define CHANGELOG_PRIV_MAGIC 0xCA8E1080
60 #define CHANGELOG_BUFFER_SZ  4096
61
62 /**
63  * Record state for efficient changelog consumption.
64  * Read chunks of CHANGELOG_BUFFER_SZ bytes.
65  */
66 struct changelog_private {
67         /* Ensure that the structure is valid and initialized */
68         int                              clp_magic;
69         /* File descriptor on the changelog character device */
70         int                              clp_fd;
71         /* Changelog delivery mode */
72         enum changelog_send_flag         clp_send_flags;
73         /* Changelog extra flags */
74         enum changelog_send_extra_flag   clp_send_extra_flags;
75         /* Available bytes in buffer */
76         size_t                           clp_buf_len;
77         /* Current position in buffer */
78         char                            *clp_buf_pos;
79         /* Read buffer with records read from system */
80         char                             clp_buf[0];
81 };
82
83 /**
84  * Start reading from a changelog
85  *
86  * @param priv      Opaque private control structure
87  * @param flags     Start flags (e.g. CHANGELOG_FLAG_JOBID)
88  * @param device    Report changes recorded on this MDT
89  * @param startrec  Report changes beginning with this record number
90  * (just call llapi_changelog_fini when done; don't need an endrec)
91  */
92 int llapi_changelog_start(void **priv, enum changelog_send_flag flags,
93                           const char *device, long long startrec)
94 {
95         struct changelog_private *cp;
96         static bool warned_extra_flags;
97         static bool warned_jobid;
98         char cdev_path[PATH_MAX];
99         int rc;
100
101         rc = chlg_dev_path(cdev_path, sizeof(cdev_path), device);
102         if (rc != 0)
103                 return rc;
104
105         /* Set up the receiver control struct */
106         cp = calloc(1, sizeof(*cp) + CHANGELOG_BUFFER_SZ);
107         if (cp == NULL)
108                 return -ENOMEM;
109
110         cp->clp_magic = CHANGELOG_PRIV_MAGIC;
111         cp->clp_send_flags = flags;
112
113         cp->clp_buf_len = 0;
114         cp->clp_buf_pos = cp->clp_buf;
115
116         /* Set up the receiver */
117         cp->clp_fd = open(cdev_path, O_RDONLY);
118         if (cp->clp_fd < 0) {
119                 rc = -errno;
120                 goto out_free_cp;
121         }
122
123         if (startrec != 0) {
124                 off_t res;
125
126                 res = lseek(cp->clp_fd, startrec, SEEK_SET);
127                 if (res == (off_t)-1) {
128                         rc = -errno;
129                         goto out_close;
130                 }
131         }
132
133         *priv = cp;
134
135         /* CHANGELOG_FLAG_EXTRA_FLAGS will eventually become mandatory.
136          * If it wasn't specified, display a warning here.
137          * Code elsewhere will remove the corresponding extension.
138          */
139         if (!(flags & CHANGELOG_FLAG_EXTRA_FLAGS) && !warned_extra_flags) {
140                 llapi_err_noerrno(LLAPI_MSG_WARN,
141                       "warning: %s() called without CHANGELOG_FLAG_EXTRA_FLAGS",
142                       __func__);
143                 warned_extra_flags = true;
144         }
145
146         /* CHANGELOG_FLAG_JOBID will eventually become mandatory. Display a
147          * warning if it's missing. */
148         if (!(flags & CHANGELOG_FLAG_JOBID) && !warned_jobid) {
149                 llapi_err_noerrno(LLAPI_MSG_WARN, "warning: %s() called "
150                                   "without CHANGELOG_FLAG_JOBID", __func__);
151                 warned_jobid = true;
152         }
153
154         if (flags & CHANGELOG_FLAG_FOLLOW) {
155                 int rc;
156                 rc = ioctl(cp->clp_fd, OBD_IOC_CHLG_POLL, 1);
157                 if (rc < 0)
158                         llapi_err_noerrno(LLAPI_MSG_ERROR, "can't enable "
159                                           "CHANGELOG_FLAG_FOLLOW");
160         }
161
162         return 0;
163
164 out_close:
165         close(cp->clp_fd);
166 out_free_cp:
167         free(cp);
168         return rc;
169 }
170
171 /** Finish reading from a changelog */
172 int llapi_changelog_fini(void **priv)
173 {
174         struct changelog_private *cp = *priv;
175
176         if (!cp || (cp->clp_magic != CHANGELOG_PRIV_MAGIC))
177                 return -EINVAL;
178
179         close(cp->clp_fd);
180         free(cp);
181         *priv = NULL;
182         return 0;
183 }
184
185 static ssize_t chlg_read_bulk(struct changelog_private *cp)
186 {
187         ssize_t rd_bytes;
188
189         if (!cp || cp->clp_magic != CHANGELOG_PRIV_MAGIC)
190                 return -EINVAL;
191
192         rd_bytes = read(cp->clp_fd, cp->clp_buf, CHANGELOG_BUFFER_SZ);
193         if (rd_bytes < 0)
194                 return -errno;
195
196         cp->clp_buf_pos = cp->clp_buf;
197         cp->clp_buf_len = rd_bytes;
198
199         return rd_bytes;
200 }
201
202 /**
203  * Returns a file descriptor to poll on.
204  *
205  * \@param[in]  priv  Opaque changelog reader structure.
206  * @return valid file descriptor on success, negated errno code on failure.
207  */
208 int llapi_changelog_get_fd(void *priv)
209 {
210         struct changelog_private *cp = priv;
211
212         if (!cp || cp->clp_magic != CHANGELOG_PRIV_MAGIC)
213                 return -EINVAL;
214
215         return cp->clp_fd;
216 }
217
218 /** Read the next changelog entry
219  * @param priv Opaque private control structure
220  * @param rech Changelog record handle; record will be allocated here
221  * @return 0 valid message received; rec is set
222  *       <0 error code
223  *       1 EOF
224  */
225 #define DEFAULT_RECORD_FMT      (CLF_VERSION | CLF_RENAME)
226 int llapi_changelog_recv(void *priv, struct changelog_rec **rech)
227 {
228         struct changelog_private *cp = priv;
229         enum changelog_rec_flags rec_fmt = DEFAULT_RECORD_FMT;
230         enum changelog_rec_extra_flags rec_extra_fmt = CLFE_INVALID;
231         struct changelog_rec *tmp;
232         int rc = 0;
233
234         if (!cp || (cp->clp_magic != CHANGELOG_PRIV_MAGIC))
235                 return -EINVAL;
236
237         if (rech == NULL)
238                 return -EINVAL;
239
240         *rech = malloc(CR_MAXSIZE);
241         if (*rech == NULL)
242                 return -ENOMEM;
243
244         if (cp->clp_send_flags & CHANGELOG_FLAG_JOBID)
245                 rec_fmt |= CLF_JOBID;
246
247         if (cp->clp_send_flags & CHANGELOG_FLAG_EXTRA_FLAGS) {
248                 rec_fmt |= CLF_EXTRA_FLAGS;
249                 if (cp->clp_send_extra_flags & CHANGELOG_EXTRA_FLAG_UIDGID)
250                         rec_extra_fmt |= CLFE_UIDGID;
251                 if (cp->clp_send_extra_flags & CHANGELOG_EXTRA_FLAG_NID)
252                         rec_extra_fmt |= CLFE_NID;
253                 if (cp->clp_send_extra_flags & CHANGELOG_EXTRA_FLAG_OMODE)
254                         rec_extra_fmt |= CLFE_OPEN;
255                 if (cp->clp_send_extra_flags & CHANGELOG_EXTRA_FLAG_XATTR)
256                         rec_extra_fmt |= CLFE_XATTR;
257         }
258
259         if (cp->clp_buf + cp->clp_buf_len <= cp->clp_buf_pos) {
260                 ssize_t refresh;
261
262                 refresh = chlg_read_bulk(cp);
263                 if (refresh == 0) {
264                         /* EOF, CHANGELOG_FLAG_FOLLOW ignored for now LU-7659 */
265                         rc = 1;
266                         goto out_free;
267                 } else if (refresh < 0) {
268                         rc = refresh;
269                         goto out_free;
270                 }
271         }
272
273         /* TODO check changelog_rec_size */
274         tmp = (struct changelog_rec *)cp->clp_buf_pos;
275
276         memcpy(*rech, cp->clp_buf_pos,
277                changelog_rec_size(tmp) + tmp->cr_namelen);
278
279         cp->clp_buf_pos += changelog_rec_size(tmp) + tmp->cr_namelen;
280         changelog_remap_rec(*rech, rec_fmt, rec_extra_fmt);
281
282         return 0;
283
284 out_free:
285         free(*rech);
286         *rech = NULL;
287         return rc;
288 }
289
290 /** Release the changelog record when done with it. */
291 int llapi_changelog_free(struct changelog_rec **rech)
292 {
293         free(*rech);
294         *rech = NULL;
295         return 0;
296 }
297
298 int llapi_changelog_in_buf(void *priv)
299 {
300         struct changelog_private *cp = priv;
301         if (cp->clp_buf + cp->clp_buf_len > cp->clp_buf_pos)
302                 return 1;
303         return 0;
304 }
305
306 int llapi_changelog_clear(const char *mdtname, const char *idstr,
307                           long long endrec)
308 {
309         char dev_path[PATH_MAX];
310         char cmd[64];
311         size_t cmd_len = sizeof(cmd);
312         char *dashp, *clidp = NULL;
313         int fd;
314         int rc;
315
316         if (endrec < 0) {
317                 llapi_err_noerrno(LLAPI_MSG_ERROR,
318                                   "can't purge negative records\n");
319                 return -EINVAL;
320         }
321
322         chlg_dev_path(dev_path, sizeof(dev_path), mdtname);
323
324         dashp = strchr(idstr, '-');
325         if (dashp) {
326                 clidp = strndup(idstr, dashp - idstr);
327                 if (!clidp)
328                         return -ENOMEM;
329         }
330
331         rc = snprintf(cmd, cmd_len, "clear:%s:%lld", dashp ? clidp : idstr,
332                       endrec);
333         if (rc >= sizeof(cmd)) {
334                 rc = -EINVAL;
335                 goto out;
336         }
337         cmd_len = rc + 1;
338
339         fd = open(dev_path, O_WRONLY);
340         if (fd < 0) {
341                 rc = -errno;
342                 llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'", dev_path);
343                 goto out;
344         }
345
346         rc = write(fd, cmd, cmd_len);
347         if (rc < 0) {
348                 rc = -errno;
349                 llapi_error(LLAPI_MSG_ERROR, rc,
350                             "cannot purge records for '%s'", idstr);
351                 goto out_close;
352         }
353
354         rc = 0;
355
356 out_close:
357         close(fd);
358 out:
359         free(clidp);
360         return rc;
361 }
362
363 /**
364  * Set extra flags for reading changelogs
365  *
366  * @param priv          Opaque private control structure
367  * @param extra_flags   Read extra flags (e.g. CHANGELOG_EXTRA_FLAG_UIDGID)
368  *
369  * Just call this function right after llapi_changelog_start().
370  */
371 int llapi_changelog_set_xflags(void *priv,
372                                enum changelog_send_extra_flag extra_flags)
373 {
374         struct changelog_private *cp = priv;
375         static bool warned_uidgid;
376
377         if (!cp || cp->clp_magic != CHANGELOG_PRIV_MAGIC)
378                 return -EINVAL;
379
380         cp->clp_send_extra_flags = extra_flags;
381
382         /* CHANGELOG_EXTRA_FLAG_UIDGID will eventually become mandatory.
383          * If it wasn't specified, display a warning here.
384          * Code elsewhere will remove the corresponding extension.
385          */
386         if (!(extra_flags & CHANGELOG_EXTRA_FLAG_UIDGID) && !warned_uidgid) {
387                 llapi_err_noerrno(LLAPI_MSG_WARN,
388                      "warning: %s() called without CHANGELOG_EXTRA_FLAG_UIDGID",
389                      __func__);
390                 warned_uidgid = true;
391         }
392
393         return 0;
394 }