Whamcloud - gitweb
LU-17612 gss: always try to unlink key in error
[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 the entry matches the saved one -> no update needed */
254                 if (strcmp(root_cached.mnt_dir, mnt.mnt_dir) == 0)
255                         goto unlock_root_cached;
256
257                 if (root_cached.fd > 0) {
258                         close(root_cached.fd);
259                         root_cached.fd = 0;
260                 }
261                 if ((want & WANT_FD) && outfd)
262                         rc = get_root_fd(mnt.mnt_dir, &root_cached.fd);
263                 strncpy(root_cached.fsname, ptr, fsnamelen);
264                 root_cached.fsname[fsnamelen] = '\0';
265                 strncpy(root_cached.mnt_dir, mnt.mnt_dir, mntlen);
266                 root_cached.mnt_dir[mntlen] = '\0';
267                 root_cached.dev = devmnt;
268                 ptr_end = strchr(mnt.mnt_fsname, ':');
269                 strncpy(root_cached.nid, mnt.mnt_fsname,
270                         ptr_end - mnt.mnt_fsname);
271                 root_cached.nid[ptr_end - mnt.mnt_fsname] = '\0';
272
273 unlock_root_cached:
274                 pthread_rwlock_unlock(&root_cached_lock);
275         }
276
277         if ((want & WANT_FSNAME) && fsname) {
278                 strncpy(fsname, ptr, fsnamelen);
279                 fsname[fsnamelen] = '\0';
280         }
281         if ((want & WANT_PATH) && path) {
282                 strncpy(path, mnt.mnt_dir, mntlen);
283                 path[mntlen] = '\0';
284         }
285         if ((want & WANT_DEV) && dev)
286                 *dev = devmnt;
287         if ((want & WANT_FD) && outfd) {
288                 if (root_cached.fd > 0)
289                         *outfd = root_cached.fd;
290                 else
291                         rc = get_root_fd(mnt.mnt_dir, outfd);
292         }
293         if ((want & WANT_NID) && nid) {
294                 ptr_end = strchr(mnt.mnt_fsname, ':');
295                 strncpy(nid, mnt.mnt_fsname, ptr_end - mnt.mnt_fsname);
296                 nid[ptr_end - mnt.mnt_fsname] = '\0';
297         }
298
299 out:
300         endmntent(fp);
301         return rc;
302 }
303
304 /*
305  * Find the fsname, the full path, and/or an open fd.
306  * Either the fsname or path must not be NULL.
307  *
308  * @outfd is for llapi internal use only, do not return it to the application.
309  */
310 int get_root_path(int want, char *fsname, int *outfd, char *path, int index,
311                   dev_t *dev, char *nid)
312 {
313         int rc = -ENODEV;
314
315         if (!(want & WANT_INDEX))
316                 rc = get_root_path_fast(want, fsname, outfd, path, dev, nid);
317         if (rc)
318                 rc = get_root_path_slow(want, fsname, outfd, path, index, dev,
319                                         nid);
320
321         if (!rc || !(want & WANT_ERROR))
322                 goto out_errno;
323
324         if (dev || !(want & WANT_DEV))
325                 llapi_err_noerrno(LLAPI_MSG_ERROR,
326                                   "'%u/%u' dev not on a mounted Lustre filesystem",
327                                   major(*dev), minor(*dev));
328         else
329                 llapi_err_noerrno(LLAPI_MSG_ERROR,
330                                   "'%s' not on a mounted Lustre filesystem",
331                                   (want & WANT_PATH) ? fsname : path);
332 out_errno:
333         errno = -rc;
334         return rc;
335 }
336 /*
337  * search lustre mounts
338  *
339  * Calling this function will return to the user the mount point, mntdir, and
340  * the file system name, fsname, if the user passed a buffer to this routine.
341  *
342  * The user inputs are pathname and index. If the pathname is supplied then
343  * the value of the index will be ignored. The pathname will return data if
344  * the pathname is located on a lustre mount. Index is used to pick which
345  * mount point you want in the case of multiple mounted lustre file systems.
346  * See function lfs_osts in lfs.c for an example of the index use.
347  */
348 int llapi_search_mounts(const char *pathname, int index, char *mntdir,
349                         char *fsname)
350 {
351         int want = WANT_PATH, idx = -1;
352
353         if (!pathname || pathname[0] == '\0') {
354                 want |= WANT_INDEX;
355                 idx = index;
356         } else {
357                 strcpy(mntdir, pathname);
358         }
359
360         if (fsname)
361                 want |= WANT_FSNAME;
362         return get_root_path(want, fsname, NULL, mntdir, idx, NULL, NULL);
363 }
364
365 /* Given a path, find the corresponding Lustre fsname */
366 int llapi_search_fsname(const char *pathname, char *fsname)
367 {
368         dev_t dev;
369         int rc;
370
371         rc = get_file_dev(pathname, &dev);
372         if (rc) {
373                 char tmp[PATH_MAX];
374                 char *parent;
375                 int len;
376
377                 /* file does not exist try the parent */
378                 len = readlink(pathname, tmp, PATH_MAX);
379                 if (len != -1)
380                         tmp[len] = '\0';
381                 else
382                         strncpy(tmp, pathname, PATH_MAX - 1);
383
384                 parent = dirname(tmp);
385                 rc = get_file_dev(parent, &dev);
386         }
387
388         if (rc) {
389                 llapi_error(LLAPI_MSG_ERROR, rc,
390                             "cannot resolve path '%s'", pathname);
391                 return rc;
392         }
393
394         rc = get_root_path(WANT_FSNAME | WANT_ERROR, fsname, NULL, NULL, -1,
395                            &dev, NULL);
396
397         return rc;
398 }
399
400
401 int llapi_search_rootpath(char *pathname, const char *fsname)
402 {
403         if (!pathname)
404                 return -EINVAL;
405
406         /*
407          * pathname can be used as an argument by get_root_path(),
408          * clear it for safety
409          */
410         pathname[0] = 0;
411         return get_root_path(WANT_PATH, (char *)fsname, NULL, pathname, -1,
412                              NULL, NULL);
413 }
414
415 int llapi_search_rootpath_by_dev(char *pathname, dev_t dev)
416 {
417         if (!pathname)
418                 return -EINVAL;
419
420         /*
421          * pathname can be used as an argument by get_root_path(),
422          * clear it for safety
423          */
424         pathname[0] = 0;
425         return get_root_path(WANT_PATH, NULL, NULL, pathname, -1, &dev, NULL);
426 }
427