Whamcloud - gitweb
LU-930 doc: add lctl-lcfg man pages
[fs/lustre-release.git] / lustre / utils / liblustreapi_root.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the GNU Lesser General Public License
8  * (LGPL) version 2.1 or (at your discretion) any later version.
9  * (LGPL) version 2.1 accompanies this distribution, and is available at
10  * http://www.gnu.org/licenses/lgpl-2.1.html
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * LGPL HEADER END
18  */
19 /*
20  * lustre/utils/liblustreapi_root.c
21  *
22  * lustreapi library for managing the root fd cache for llapi internal use.
23  *
24  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
25  * Use is subject to license terms.
26  *
27  * Copyright (c) 2011, 2017, Intel Corporation.
28  *
29  * Copyright (c) 2018, 2022, Data Direct Networks
30  */
31
32 /* for O_DIRECTORY and struct file_handle */
33 #ifndef _GNU_SOURCE
34 #define _GNU_SOURCE
35 #endif
36
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <libgen.h> /* for dirname() */
40 #include <mntent.h>
41 #include <pthread.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/ioctl.h>
46 #include <sys/stat.h>
47 #include <sys/sysmacros.h> /* for makedev() */
48 #include <sys/types.h>
49 #include <unistd.h>
50
51 #include <libcfs/util/ioctl.h>
52 #include <lustre/lustreapi.h>
53 #include <linux/lustre/lustre_fid.h>
54 #include "lustreapi_internal.h"
55
56 /* could have an array of these for a handful of different paths */
57 static struct root_cache {
58         dev_t   dev;
59         char    fsname[PATH_MAX];
60         char    mnt_dir[PATH_MAX];
61         char    nid[MAX_LINE_LEN];
62         int     fd; /* cached fd on filesystem root for internal use only */
63 } root_cached = { 0 };
64
65 static pthread_rwlock_t root_cached_lock = PTHREAD_RWLOCK_INITIALIZER;
66
67 static int get_root_fd(const char *rootpath, int *outfd)
68 {
69         int rc = 0;
70         int fd;
71
72         fd = open(rootpath, O_RDONLY | O_DIRECTORY | O_NONBLOCK);
73         if (fd < 0) {
74                 rc = -errno;
75                 llapi_error(LLAPI_MSG_ERROR, rc,
76                             "cannot open '%s'", rootpath);
77         } else {
78                 *outfd = fd;
79         }
80
81         return rc;
82 }
83
84 static int get_file_dev(const char *path, dev_t *dev)
85 {
86 #ifdef HAVE_STATX
87         struct statx stx;
88
89         if (!dev)
90                 return -EINVAL;
91         if (statx(AT_FDCWD, path, 0, 0, &stx))
92                 return -errno;
93         *dev = makedev(stx.stx_dev_major, stx.stx_dev_minor);
94 #else
95         struct stat st;
96
97         if (!dev)
98                 return -EINVAL;
99         if (stat(path, &st) != 0)
100                 return -errno;
101
102         *dev = st.st_dev;
103 #endif
104         return 0;
105 }
106
107 static int get_root_path_fast(int want, char *fsname, int *outfd, char *path,
108                               dev_t *dev, char *nid)
109 {
110         int rc = -ENODEV;
111         int fsnamelen;
112         int mntlen;
113
114         if (root_cached.dev == 0)
115                 return rc;
116
117         /* hold a write lock on the cache if fd is going to be updated */
118         if ((want & WANT_FD) && outfd && root_cached.fd <= 0)
119                 pthread_rwlock_wrlock(&root_cached_lock);
120         else
121                 pthread_rwlock_rdlock(&root_cached_lock);
122
123         if (root_cached.dev == 0)
124                 goto out_unlock;
125
126         fsnamelen = strlen(root_cached.fsname);
127         mntlen = strlen(root_cached.mnt_dir);
128
129         /* Check the dev for a match, if given */
130         if (!(want & WANT_DEV) && dev && *dev == root_cached.dev) {
131                 rc = 0;
132         /* Check the fsname for a match, if given */
133         } else if (!(want & WANT_FSNAME) && fsname &&
134                    strlen(fsname) == fsnamelen &&
135                    (strncmp(root_cached.fsname, fsname, fsnamelen) == 0)) {
136                 rc = 0;
137         /* Otherwise find the longest matching path */
138         } else if (path && strlen(path) >= mntlen &&
139                    (strncmp(root_cached.mnt_dir, path, mntlen) == 0) &&
140                    (strlen(path) == mntlen || path[mntlen] == '/')) {
141                 rc = 0;
142         }
143
144         if (rc)
145                 goto out_unlock;
146
147         if ((want & WANT_FSNAME) && fsname)
148                 strcpy(fsname, root_cached.fsname);
149         if ((want & WANT_PATH) && path)
150                 strcpy(path, root_cached.mnt_dir);
151         if ((want & WANT_DEV) && dev)
152                 *dev = root_cached.dev;
153         if ((want & WANT_FD) && outfd) {
154                 if (root_cached.fd > 0) {
155                         *outfd = root_cached.fd;
156                 } else {
157                         rc = get_root_fd(root_cached.mnt_dir, outfd);
158                         if (!rc)
159                                 root_cached.fd = *outfd;
160                 }
161         }
162         if ((want & WANT_NID) && nid)
163                 strcpy(nid, root_cached.nid);
164 out_unlock:
165         pthread_rwlock_unlock(&root_cached_lock);
166
167         return rc;
168 }
169
170 static int get_root_path_slow(int want, char *fsname, int *outfd, char *path,
171                               int index, dev_t *dev, char *nid)
172 {
173         struct mntent mnt;
174         char buf[PATH_MAX];
175         char *ptr, *ptr_end;
176         FILE *fp;
177         int idx = -1, mntlen = 0;
178         int rc = -ENODEV;
179         int fsnamelen = 0;
180         dev_t devmnt = 0;
181
182         /* get the mount point */
183         fp = setmntent(PROC_MOUNTS, "r");
184         if (fp == NULL) {
185                 rc = -EIO;
186                 llapi_error(LLAPI_MSG_ERROR, rc,
187                             "cannot retrieve filesystem mount point");
188                 return rc;
189         }
190         while (getmntent_r(fp, &mnt, buf, sizeof(buf))) {
191
192                 if (!llapi_is_lustre_mnt(&mnt))
193                         continue;
194
195                 idx++;
196                 mntlen = strlen(mnt.mnt_dir);
197                 ptr = strchr(mnt.mnt_fsname, '/');
198                 while (ptr && *ptr == '/')
199                         ptr++;
200                 /*
201                  * thanks to the call to llapi_is_lustre_mnt() above,
202                  * we are sure that mnt.mnt_fsname contains ":/",
203                  * so ptr should never be NULL
204                  */
205                 if (ptr == NULL)
206                         continue;
207                 ptr_end = ptr;
208                 while (*ptr_end != '/' && *ptr_end != '\0')
209                         ptr_end++;
210
211                 fsnamelen = ptr_end - ptr;
212
213                 /* ignore unaccessible filesystem */
214                 if (get_file_dev(mnt.mnt_dir, &devmnt))
215                         continue;
216
217                 if ((want & WANT_INDEX) && idx == index) {
218                         rc = 0;
219                         break;
220                 }
221
222                 /* Check the fsname for a match, if given */
223                 if (!(want & WANT_FSNAME) && fsname &&
224                     strlen(fsname) == fsnamelen &&
225                     (strncmp(ptr, fsname, fsnamelen) == 0)) {
226                         rc = 0;
227                         break;
228                 }
229
230                 /* Check the dev for a match, if given */
231                 if (!(want & WANT_DEV) && dev && *dev == devmnt) {
232                         rc = 0;
233                         break;
234                 }
235
236                 /* Otherwise find the longest matching path */
237                 if (path && strlen(path) >= mntlen &&
238                     (strncmp(mnt.mnt_dir, path, mntlen) == 0) &&
239                     (strlen(path) == mntlen || path[mntlen] == '/')) {
240                         rc = 0;
241                         break;
242                 }
243         }
244
245         if (rc)
246                 goto out;
247
248         /* Found it */
249         if (!(want & WANT_INDEX)) {
250                 /* Cache the mount point information */
251                 pthread_rwlock_wrlock(&root_cached_lock);
252
253                 if (root_cached.fd > 0) {
254                         close(root_cached.fd);
255                         root_cached.fd = 0;
256                 }
257                 if ((want & WANT_FD) && outfd)
258                         rc = get_root_fd(mnt.mnt_dir, &root_cached.fd);
259                 strncpy(root_cached.fsname, ptr, fsnamelen);
260                 root_cached.fsname[fsnamelen] = '\0';
261                 strncpy(root_cached.mnt_dir, mnt.mnt_dir, mntlen);
262                 root_cached.mnt_dir[mntlen] = '\0';
263                 root_cached.dev = devmnt;
264                 ptr_end = strchr(mnt.mnt_fsname, ':');
265                 strncpy(root_cached.nid, mnt.mnt_fsname,
266                         ptr_end - mnt.mnt_fsname);
267                 root_cached.nid[ptr_end - mnt.mnt_fsname] = '\0';
268
269                 pthread_rwlock_unlock(&root_cached_lock);
270         }
271
272         if ((want & WANT_FSNAME) && fsname) {
273                 strncpy(fsname, ptr, fsnamelen);
274                 fsname[fsnamelen] = '\0';
275         }
276         if ((want & WANT_PATH) && path) {
277                 strncpy(path, mnt.mnt_dir, mntlen);
278                 path[mntlen] = '\0';
279         }
280         if ((want & WANT_DEV) && dev)
281                 *dev = devmnt;
282         if ((want & WANT_FD) && outfd) {
283                 if (root_cached.fd > 0)
284                         *outfd = root_cached.fd;
285                 else
286                         rc = get_root_fd(mnt.mnt_dir, outfd);
287         }
288         if ((want & WANT_NID) && nid) {
289                 ptr_end = strchr(mnt.mnt_fsname, ':');
290                 strncpy(nid, mnt.mnt_fsname, ptr_end - mnt.mnt_fsname);
291                 nid[ptr_end - mnt.mnt_fsname] = '\0';
292         }
293
294 out:
295         endmntent(fp);
296         return rc;
297 }
298
299 /*
300  * Find the fsname, the full path, and/or an open fd.
301  * Either the fsname or path must not be NULL.
302  *
303  * @outfd is for llapi internal use only, do not return it to the application.
304  */
305 int get_root_path(int want, char *fsname, int *outfd, char *path, int index,
306                   dev_t *dev, char *nid)
307 {
308         int rc = -ENODEV;
309
310         if (!(want & WANT_INDEX))
311                 rc = get_root_path_fast(want, fsname, outfd, path, dev, nid);
312         if (rc)
313                 rc = get_root_path_slow(want, fsname, outfd, path, index, dev,
314                                         nid);
315
316         if (!rc || !(want & WANT_ERROR))
317                 goto out_errno;
318
319         if (dev || !(want & WANT_DEV))
320                 llapi_err_noerrno(LLAPI_MSG_ERROR,
321                                   "'%u/%u' dev not on a mounted Lustre filesystem",
322                                   major(*dev), minor(*dev));
323         else
324                 llapi_err_noerrno(LLAPI_MSG_ERROR,
325                                   "'%s' not on a mounted Lustre filesystem",
326                                   (want & WANT_PATH) ? fsname : path);
327 out_errno:
328         errno = -rc;
329         return rc;
330 }
331 /*
332  * search lustre mounts
333  *
334  * Calling this function will return to the user the mount point, mntdir, and
335  * the file system name, fsname, if the user passed a buffer to this routine.
336  *
337  * The user inputs are pathname and index. If the pathname is supplied then
338  * the value of the index will be ignored. The pathname will return data if
339  * the pathname is located on a lustre mount. Index is used to pick which
340  * mount point you want in the case of multiple mounted lustre file systems.
341  * See function lfs_osts in lfs.c for an example of the index use.
342  */
343 int llapi_search_mounts(const char *pathname, int index, char *mntdir,
344                         char *fsname)
345 {
346         int want = WANT_PATH, idx = -1;
347
348         if (!pathname || pathname[0] == '\0') {
349                 want |= WANT_INDEX;
350                 idx = index;
351         } else {
352                 strcpy(mntdir, pathname);
353         }
354
355         if (fsname)
356                 want |= WANT_FSNAME;
357         return get_root_path(want, fsname, NULL, mntdir, idx, NULL, NULL);
358 }
359
360 /* Given a path, find the corresponding Lustre fsname */
361 int llapi_search_fsname(const char *pathname, char *fsname)
362 {
363         dev_t dev;
364         int rc;
365
366         rc = get_file_dev(pathname, &dev);
367         if (rc) {
368                 char tmp[PATH_MAX];
369                 char *parent;
370                 int len;
371
372                 /* file does not exist try the parent */
373                 len = readlink(pathname, tmp, PATH_MAX);
374                 if (len != -1)
375                         tmp[len] = '\0';
376                 else
377                         strncpy(tmp, pathname, PATH_MAX - 1);
378
379                 parent = dirname(tmp);
380                 rc = get_file_dev(parent, &dev);
381         }
382
383         if (rc) {
384                 llapi_error(LLAPI_MSG_ERROR, rc,
385                             "cannot resolve path '%s'", pathname);
386                 return rc;
387         }
388
389         rc = get_root_path(WANT_FSNAME | WANT_ERROR, fsname, NULL, NULL, -1,
390                            &dev, NULL);
391
392         return rc;
393 }
394
395
396 int llapi_search_rootpath(char *pathname, const char *fsname)
397 {
398         if (!pathname)
399                 return -EINVAL;
400
401         /*
402          * pathname can be used as an argument by get_root_path(),
403          * clear it for safety
404          */
405         pathname[0] = 0;
406         return get_root_path(WANT_PATH, (char *)fsname, NULL, pathname, -1,
407                              NULL, NULL);
408 }
409
410 int llapi_search_rootpath_by_dev(char *pathname, dev_t dev)
411 {
412         if (!pathname)
413                 return -EINVAL;
414
415         /*
416          * pathname can be used as an argument by get_root_path(),
417          * clear it for safety
418          */
419         pathname[0] = 0;
420         return get_root_path(WANT_PATH, NULL, NULL, pathname, -1, &dev, NULL);
421 }
422