Whamcloud - gitweb
LU-16335 build: remove _GNU_SOURCE dependency in lustre_user.h
[fs/lustre-release.git] / lustre / utils / liblustreapi_util.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * (C) Copyright (c) 2015, Cray Inc, all rights reserved.
7  *
8  * Copyright (c) 2016, 2017, Intel Corporation.
9  *
10  * All rights reserved. This program and the accompanying materials
11  * are made available under the terms of the GNU Lesser General Public License
12  * LGPL version 2.1 or (at your discretion) any later version.
13  * LGPL version 2.1 accompanies this distribution, and is available at
14  * http://www.gnu.org/licenses/lgpl-2.1.html
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * LGPL HEADER END
22  */
23 /*
24  * lustre/utils/liblustreapi_util.c
25  *
26  * Misc LGPL-licenced utility functions for liblustreapi.
27  *
28  * Author: Frank Zago <fzago@cray.com>
29  */
30
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdbool.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <sys/ioctl.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/time.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/syscall.h>
44 #include <libgen.h> /* for dirname() */
45 #include <lustre/lustreapi.h>
46 #include <linux/lustre/lustre_ver.h>    /* only until LUSTRE_VERSION_CODE is gone */
47 #include "lustreapi_internal.h"
48
49 /*
50  * Indicate whether the liblustreapi_init() constructor below has run or not.
51  *
52  * This can be used by external programs to ensure that the initialization
53  * mechanism has actually worked.
54  */
55 bool liblustreapi_initialized;
56
57 /**
58  * Initialize the library once at startup.
59  *
60  * Initializes the random number generator (random()). Get
61  * data from different places in case one of them fails. This
62  * is enough to get reasonably random numbers, but is not
63  * strong enough to be used for cryptography.
64  */
65 static __attribute__ ((constructor)) void liblustreapi_init(void)
66 {
67         unsigned int    seed;
68         struct timeval  tv;
69         int             fd;
70
71         seed = syscall(SYS_gettid);
72
73         if (gettimeofday(&tv, NULL) == 0) {
74                 seed ^= tv.tv_sec;
75                 seed ^= tv.tv_usec;
76         }
77
78         fd = open("/dev/urandom", O_RDONLY | O_NOFOLLOW);
79         if (fd >= 0) {
80                 unsigned int rnumber;
81                 ssize_t ret;
82
83                 ret = read(fd, &rnumber, sizeof(rnumber));
84                 seed ^= rnumber ^ ret;
85                 close(fd);
86         }
87
88         srandom(seed);
89         liblustreapi_initialized = true;
90 }
91
92 /**
93  * Return the release version for the Lustre modules, e.g. 2.6.92.
94  *
95  * The "version" file in /proc currently returns only the line:
96  * lustre: 2.8.52
97  *
98  * but in the past it also returned more lines that should be ignored:
99  * kernel: patchless_client
100  * build: v2_6_92_0-gadb3ee4-2.6.32-431.29.2.el6_lustre.g36cd22b.x86_64
101  *
102  * \param version[in,out]       buffer to store build version string
103  * \param version_size[in]      size of \a version
104  *
105  * \retval                      0 on success
106  * \retval                      -1 on failure, errno set
107  */
108 int llapi_get_version_string(char *version, unsigned int version_size)
109 {
110         char buffer[4096];
111         char *ptr;
112         int rc;
113
114         if (version == NULL || version_size == 0) {
115                 errno = EINVAL;
116                 return -1;
117         }
118
119         rc = get_lustre_param_value(NULL, NULL, FILTER_BY_NONE, "version",
120                                     buffer, sizeof(buffer));
121         if (rc < 0) {
122                 errno = -rc;
123                 return -1;
124         }
125
126         ptr = strstr(buffer, "lustre:");
127         if (ptr) {
128                 ptr += strlen("lustre:");
129                 while (*ptr == ' ' || *ptr == '\t')
130                         ptr++;
131         } else {
132                 ptr = buffer;
133         }
134         llapi_chomp_string(ptr);
135
136         if (ptr[0] == '\0') {
137                 errno = ENODATA;
138                 return -1;
139         }
140
141         if (snprintf(version, version_size, "%s", ptr) >= version_size) {
142                 errno = EOVERFLOW;
143                 return -1;
144         }
145         return 0;
146 }
147
148 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 4, 53, 0)
149 /**
150  * Return the build version of the Lustre code.
151  *
152  * The **version argument is pointless, so llapi_get_version_string() is
153  * better to use in the future, but give users a few versions to fix * it.
154  *
155  * \param buffer[in]            temporary buffer to hold version string
156  * \param buffer_size[in]       length of the \a buffer
157  * \param version[out]          pointer to the start of build version string
158  *
159  * \retval                      0 on success
160  * \retval                      -ve errno on failure
161  */
162 int llapi_get_version(char *buffer, int buffer_size, char **version)
163 {
164         int rc;
165 #if LUSTRE_VERSION_CODE > OBD_OCD_VERSION(2, 8, 53, 0)
166         static bool printed;
167         if (!printed) {
168                 fprintf(stderr,
169                         "%s deprecated, use llapi_get_version_string()\n",
170                         __func__);
171                 printed = true;
172         }
173 #endif
174
175         rc = llapi_get_version_string(buffer, buffer_size);
176         /* keep old return style for this legacy function */
177         if (rc == -1)
178                 rc = -errno;
179         else
180                 *version = buffer;
181
182         return rc;
183 }
184 #endif /* LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 4, 53, 0) */
185
186 /*
187  * fsname must be specified
188  * if poolname is NULL, search tgtname in fsname
189  * if poolname is not NULL:
190  *  if poolname not found returns errno < 0
191  *  if tgtname is NULL, returns 1 if pool is not empty and 0 if pool empty
192  *  if tgtname is not NULL, returns 1 if target is in pool and 0 if not
193  */
194 int llapi_search_tgt(const char *fsname, const char *poolname,
195                      const char *tgtname, bool is_mdt)
196 {
197         char buffer[PATH_MAX];
198         size_t len = 0;
199         glob_t param;
200         FILE *fd;
201         int rc;
202
203         if (fsname && fsname[0] == '\0')
204                 fsname = NULL;
205         if (!fsname) {
206                 rc = -EINVAL;
207                 goto out;
208         }
209
210         if (poolname && poolname[0] == '\0')
211                 poolname = NULL;
212         if (tgtname) {
213                 if (tgtname[0] == '\0')
214                         tgtname = NULL;
215                 else
216                         len = strlen(tgtname);
217         }
218
219         /* You need one or the other to have something in it */
220         if (!poolname && !tgtname) {
221                 rc = -EINVAL;
222                 goto out;
223         }
224
225         if (poolname) {
226                 rc = poolpath(&param, fsname, NULL);
227                 if (!rc) {
228                         snprintf(buffer, sizeof(buffer) - 1, "%s/%s",
229                                  param.gl_pathv[0], poolname);
230                         buffer[sizeof(buffer) - 1] = '\0';
231                 }
232         } else {
233                 rc = get_lustre_param_path(is_mdt ? "lmv" : "lov", fsname,
234                                            FILTER_BY_FS_NAME,
235                                            "target_obd", &param);
236                 if (!rc) {
237                         strncpy(buffer, param.gl_pathv[0],
238                                 sizeof(buffer) - 1);
239                         buffer[sizeof(buffer) - 1] = '\0';
240                 }
241         }
242         cfs_free_param_data(&param);
243         if (rc)
244                 goto out;
245
246         fd = fopen(buffer, "r");
247         if (!fd) {
248                 rc = -errno;
249                 goto out;
250         }
251
252         while (fgets(buffer, sizeof(buffer), fd)) {
253                 if (!poolname) {
254                         char *ptr;
255                         /* Search for an tgtname in the list of all targets
256                          * Line format is IDX: fsname-OST/MDTxxxx_UUID STATUS */
257                         ptr = strchr(buffer, ' ');
258                         if (ptr && strncmp(ptr + 1, tgtname, len) == 0) {
259                                 rc = 1;
260                                 goto out_close;
261                         }
262                 } else {
263                         /* Search for an tgtname in a pool,
264                          * (or an existing non-empty pool if no tgtname) */
265                         if (!tgtname || strncmp(buffer, tgtname, len) == 0) {
266                                 rc = 1;
267                                 goto out_close;
268                         }
269                 }
270         }
271 out_close:
272         fclose(fd);
273 out:
274         if (rc < 0)
275                 errno = -rc;
276         return rc;
277 }
278
279 int llapi_search_mdt(const char *fsname, const char *poolname,
280                      const char *mdtname)
281 {
282         return llapi_search_tgt(fsname, poolname, mdtname, true);
283 }
284
285 int llapi_search_ost(const char *fsname, const char *poolname,
286                      const char *ostname)
287 {
288         return llapi_search_tgt(fsname, poolname, ostname, false);
289 }
290
291 int llapi_rmfid(const char *path, struct fid_array *fa)
292 {
293         char rootpath[PATH_MAX];
294         int fd, rc;
295
296 retry_open:
297         fd = open(path, O_RDONLY | O_NONBLOCK | O_NOFOLLOW);
298         if (fd < 0) {
299                 if (errno == ENOENT && path != rootpath) {
300                         rc = llapi_search_rootpath(rootpath, path);
301                         if (!rc) {
302                                 path = rootpath;
303                                 goto retry_open;
304                         }
305                 } else {
306                         return -errno;
307                 }
308         }
309
310         rc = ioctl(fd, LL_IOC_RMFID, fa);
311         close(fd);
312
313         return rc ? -errno : 0;
314 }
315
316 int llapi_direntry_remove(char *dname)
317 {
318 #ifdef LL_IOC_REMOVE_ENTRY
319         char *dirpath = NULL;
320         char *namepath = NULL;
321         char *dir;
322         char *filename;
323         int fd = -1;
324         int rc = 0;
325
326         dirpath = strdup(dname);
327         namepath = strdup(dname);
328         if (!dirpath || !namepath)
329                 return -ENOMEM;
330
331         filename = basename(namepath);
332
333         dir = dirname(dirpath);
334
335         fd = open(dir, O_DIRECTORY | O_RDONLY);
336         if (fd < 0) {
337                 rc = -errno;
338                 llapi_error(LLAPI_MSG_ERROR, rc, "unable to open '%s'",
339                             filename);
340                 goto out;
341         }
342
343         if (ioctl(fd, LL_IOC_REMOVE_ENTRY, filename))
344                 llapi_error(LLAPI_MSG_ERROR, errno,
345                             "error on ioctl %#lx for '%s' (%d)",
346                             (long)LL_IOC_LMV_SETSTRIPE, filename, fd);
347 out:
348         free(dirpath);
349         free(namepath);
350         if (fd != -1)
351                 close(fd);
352         return rc;
353 #else
354         return -EOPNOTSUPP;
355 #endif
356 }
357
358 int llapi_unlink_foreign(char *name)
359 {
360         int fd = -1;
361         int rc = 0;
362
363         fd = open(name, O_DIRECTORY | O_RDONLY | O_NOFOLLOW);
364         if (fd < 0 && errno != ENOTDIR) {
365                 rc = -errno;
366                 llapi_error(LLAPI_MSG_ERROR, rc, "unable to open '%s'", name);
367                 goto out;
368         } else if (errno == ENOTDIR) {
369                 fd = open(name, O_RDONLY | O_NOFOLLOW);
370                 if (fd < 0) {
371                         rc = -errno;
372                         llapi_error(LLAPI_MSG_ERROR, rc, "unable to open '%s'",
373                                     name);
374                         goto out;
375                 }
376         }
377
378         /* allow foreign symlink file/dir to be unlinked */
379         if (ioctl(fd, LL_IOC_UNLOCK_FOREIGN)) {
380                 llapi_error(LLAPI_MSG_ERROR, errno,
381                             "error on ioctl %#lx for '%s' (%d)",
382                             (long)LL_IOC_UNLOCK_FOREIGN, name, fd);
383                 rc = -errno;
384         }
385
386         /* XXX do not set AT_REMOVEDIR in flags even for a dir, as due to the
387          * hack for foreign symlink it will fail the directory check in
388          * Kernel's syscall code and return ENOTDIR, so treat all as files
389          */
390         rc = unlinkat(AT_FDCWD, name, 0);
391         if (rc == -1 && errno == EISDIR)
392                 rc = unlinkat(AT_FDCWD, name, AT_REMOVEDIR);
393
394         if (rc == -1) {
395                 llapi_error(LLAPI_MSG_ERROR, errno,
396                             "error on unlinkat for '%s' (%d)", name, fd);
397                 rc = -errno;
398         }
399
400 out:
401         if (fd != -1)
402                 close(fd);
403         return rc;
404 }
405
406 int llapi_get_fsname_instance(const char *path, char *fsname, size_t fsname_len,
407                               char *instance, size_t instance_len)
408 {
409         struct obd_uuid uuid_buf;
410         char *uuid = uuid_buf.uuid;
411         char *ptr;
412         int rc;
413
414         memset(&uuid_buf, 0, sizeof(uuid_buf));
415         rc = llapi_file_get_lov_uuid(path, &uuid_buf);
416         if (rc)
417                 return rc;
418
419         /*
420          * We want to turn fs-foo-clilov-ffff88002738bc00 into 'fs-foo' and
421          * 'ffff88002738bc00' in a portable way that doesn't depend on what is
422          * after "-clilov-" as it may change to a UUID string in the future.
423          * Unfortunately, the "fsname" part may contain a dash, so we can't
424          * just skip to the first dash, and if the "instance" is a UUID in the
425          * future we can't necessarily go to the last dash either.
426          */
427         ptr = strstr(uuid, "-clilov-");
428         if (!ptr || (!fsname && !instance)) {
429                 rc = -EINVAL;
430                 goto out;
431         }
432
433         *ptr = '\0';
434         ptr += strlen("-clilov-");
435         if (instance) {
436                 snprintf(instance, instance_len, "%s", ptr);
437                 if (strlen(ptr) >= instance_len)
438                         rc = -ENAMETOOLONG;
439         }
440
441         if (fsname) {
442                 snprintf(fsname, fsname_len, "%s", uuid);
443                 if (strlen(uuid) >= fsname_len)
444                         rc = -ENAMETOOLONG;
445         }
446
447 out:
448         errno = -rc;
449         return rc;
450 }
451
452 int llapi_getname(const char *path, char *name, size_t namelen)
453 {
454         char fsname[16];
455         char instance[40];
456         int rc;
457
458         rc = llapi_get_fsname_instance(path, fsname, sizeof(fsname),
459                                        instance, sizeof(instance));
460         if (rc)
461                 return rc;
462
463         snprintf(name, namelen, "%s-%s", fsname, instance);
464         if (strlen(fsname) + 1 + strlen(instance) >= namelen) {
465                 rc = -ENAMETOOLONG;
466                 errno = -rc;
467         }
468
469         return rc;
470 }
471
472 int llapi_get_instance(const char *path, char *instance, size_t instance_len)
473 {
474         return llapi_get_fsname_instance(path, NULL, 0, instance, instance_len);
475 }
476
477 int llapi_get_fsname(const char *path, char *fsname, size_t fsname_len)
478 {
479         return llapi_get_fsname_instance(path, fsname, fsname_len, NULL, 0);
480 }