4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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
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.
20 * lustre/utils/liblustreapi_root.c
22 * lustreapi library for managing the root fd cache for llapi internal use.
24 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
25 * Use is subject to license terms.
27 * Copyright (c) 2011, 2017, Intel Corporation.
29 * Copyright (c) 2018, 2022, Data Direct Networks
32 /* for O_DIRECTORY and struct file_handle */
39 #include <libgen.h> /* for dirname() */
45 #include <sys/ioctl.h>
47 #include <sys/sysmacros.h> /* for makedev() */
48 #include <sys/types.h>
51 #include <libcfs/util/ioctl.h>
52 #include <lustre/lustreapi.h>
53 #include <linux/lustre/lustre_fid.h>
54 #include "lustreapi_internal.h"
56 /* could have an array of these for a handful of different paths */
57 static struct root_cache {
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 };
65 static pthread_rwlock_t root_cached_lock = PTHREAD_RWLOCK_INITIALIZER;
67 static int get_root_fd(const char *rootpath, int *outfd)
72 fd = open(rootpath, O_RDONLY | O_DIRECTORY | O_NONBLOCK);
75 llapi_error(LLAPI_MSG_ERROR, rc,
76 "cannot open '%s'", rootpath);
84 static int get_file_dev(const char *path, dev_t *dev)
91 if (statx(AT_FDCWD, path, 0, 0, &stx))
93 *dev = makedev(stx.stx_dev_major, stx.stx_dev_minor);
99 if (stat(path, &st) != 0)
107 static int get_root_path_fast(int want, char *fsname, int *outfd, char *path,
108 dev_t *dev, char *nid)
114 if (root_cached.dev == 0)
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);
121 pthread_rwlock_rdlock(&root_cached_lock);
123 if (root_cached.dev == 0)
126 fsnamelen = strlen(root_cached.fsname);
127 mntlen = strlen(root_cached.mnt_dir);
129 /* Check the dev for a match, if given */
130 if (!(want & WANT_DEV) && dev && *dev == root_cached.dev) {
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)) {
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] == '/')) {
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;
157 rc = get_root_fd(root_cached.mnt_dir, outfd);
159 root_cached.fd = *outfd;
162 if ((want & WANT_NID) && nid)
163 strcpy(nid, root_cached.nid);
165 pthread_rwlock_unlock(&root_cached_lock);
170 static int get_root_path_slow(int want, char *fsname, int *outfd, char *path,
171 int index, dev_t *dev, char *nid)
177 int idx = -1, mntlen = 0;
182 /* get the mount point */
183 fp = setmntent(PROC_MOUNTS, "r");
186 llapi_error(LLAPI_MSG_ERROR, rc,
187 "cannot retrieve filesystem mount point");
190 while (getmntent_r(fp, &mnt, buf, sizeof(buf))) {
192 if (!llapi_is_lustre_mnt(&mnt))
196 mntlen = strlen(mnt.mnt_dir);
197 ptr = strchr(mnt.mnt_fsname, '/');
198 while (ptr && *ptr == '/')
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
208 while (*ptr_end != '/' && *ptr_end != '\0')
211 fsnamelen = ptr_end - ptr;
213 /* ignore unaccessible filesystem */
214 if (get_file_dev(mnt.mnt_dir, &devmnt))
217 if ((want & WANT_INDEX) && idx == index) {
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)) {
230 /* Check the dev for a match, if given */
231 if (!(want & WANT_DEV) && dev && *dev == devmnt) {
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] == '/')) {
249 if (!(want & WANT_INDEX)) {
250 /* Cache the mount point information */
251 pthread_rwlock_wrlock(&root_cached_lock);
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;
257 if (root_cached.fd > 0) {
258 close(root_cached.fd);
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';
274 pthread_rwlock_unlock(&root_cached_lock);
277 if ((want & WANT_FSNAME) && fsname) {
278 strncpy(fsname, ptr, fsnamelen);
279 fsname[fsnamelen] = '\0';
281 if ((want & WANT_PATH) && path) {
282 strncpy(path, mnt.mnt_dir, mntlen);
285 if ((want & WANT_DEV) && dev)
287 if ((want & WANT_FD) && outfd) {
288 if (root_cached.fd > 0)
289 *outfd = root_cached.fd;
291 rc = get_root_fd(mnt.mnt_dir, outfd);
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';
305 * Find the fsname, the full path, and/or an open fd.
306 * Either the fsname or path must not be NULL.
308 * @outfd is for llapi internal use only, do not return it to the application.
310 int get_root_path(int want, char *fsname, int *outfd, char *path, int index,
311 dev_t *dev, char *nid)
315 if (!(want & WANT_INDEX))
316 rc = get_root_path_fast(want, fsname, outfd, path, dev, nid);
318 rc = get_root_path_slow(want, fsname, outfd, path, index, dev,
321 if (!rc || !(want & WANT_ERROR))
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));
329 llapi_err_noerrno(LLAPI_MSG_ERROR,
330 "'%s' not on a mounted Lustre filesystem",
331 (want & WANT_PATH) ? fsname : path);
337 * search lustre mounts
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.
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.
348 int llapi_search_mounts(const char *pathname, int index, char *mntdir,
351 int want = WANT_PATH, idx = -1;
353 if (!pathname || pathname[0] == '\0') {
357 strcpy(mntdir, pathname);
362 return get_root_path(want, fsname, NULL, mntdir, idx, NULL, NULL);
365 /* Given a path, find the corresponding Lustre fsname */
366 int llapi_search_fsname(const char *pathname, char *fsname)
371 rc = get_file_dev(pathname, &dev);
377 /* file does not exist try the parent */
378 len = readlink(pathname, tmp, PATH_MAX);
382 strncpy(tmp, pathname, PATH_MAX - 1);
384 parent = dirname(tmp);
385 rc = get_file_dev(parent, &dev);
389 llapi_error(LLAPI_MSG_ERROR, rc,
390 "cannot resolve path '%s'", pathname);
394 rc = get_root_path(WANT_FSNAME | WANT_ERROR, fsname, NULL, NULL, -1,
401 int llapi_search_rootpath(char *pathname, const char *fsname)
407 * pathname can be used as an argument by get_root_path(),
408 * clear it for safety
411 return get_root_path(WANT_PATH, (char *)fsname, NULL, pathname, -1,
415 int llapi_search_rootpath_by_dev(char *pathname, dev_t dev)
421 * pathname can be used as an argument by get_root_path(),
422 * clear it for safety
425 return get_root_path(WANT_PATH, NULL, NULL, pathname, -1, &dev, NULL);