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 (root_cached.fd > 0) {
254 close(root_cached.fd);
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';
269 pthread_rwlock_unlock(&root_cached_lock);
272 if ((want & WANT_FSNAME) && fsname) {
273 strncpy(fsname, ptr, fsnamelen);
274 fsname[fsnamelen] = '\0';
276 if ((want & WANT_PATH) && path) {
277 strncpy(path, mnt.mnt_dir, mntlen);
280 if ((want & WANT_DEV) && dev)
282 if ((want & WANT_FD) && outfd) {
283 if (root_cached.fd > 0)
284 *outfd = root_cached.fd;
286 rc = get_root_fd(mnt.mnt_dir, outfd);
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';
300 * Find the fsname, the full path, and/or an open fd.
301 * Either the fsname or path must not be NULL.
303 * @outfd is for llapi internal use only, do not return it to the application.
305 int get_root_path(int want, char *fsname, int *outfd, char *path, int index,
306 dev_t *dev, char *nid)
310 if (!(want & WANT_INDEX))
311 rc = get_root_path_fast(want, fsname, outfd, path, dev, nid);
313 rc = get_root_path_slow(want, fsname, outfd, path, index, dev,
316 if (!rc || !(want & WANT_ERROR))
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));
324 llapi_err_noerrno(LLAPI_MSG_ERROR,
325 "'%s' not on a mounted Lustre filesystem",
326 (want & WANT_PATH) ? fsname : path);
332 * search lustre mounts
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.
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.
343 int llapi_search_mounts(const char *pathname, int index, char *mntdir,
346 int want = WANT_PATH, idx = -1;
348 if (!pathname || pathname[0] == '\0') {
352 strcpy(mntdir, pathname);
357 return get_root_path(want, fsname, NULL, mntdir, idx, NULL, NULL);
360 /* Given a path, find the corresponding Lustre fsname */
361 int llapi_search_fsname(const char *pathname, char *fsname)
366 rc = get_file_dev(pathname, &dev);
372 /* file does not exist try the parent */
373 len = readlink(pathname, tmp, PATH_MAX);
377 strncpy(tmp, pathname, PATH_MAX - 1);
379 parent = dirname(tmp);
380 rc = get_file_dev(parent, &dev);
384 llapi_error(LLAPI_MSG_ERROR, rc,
385 "cannot resolve path '%s'", pathname);
389 rc = get_root_path(WANT_FSNAME | WANT_ERROR, fsname, NULL, NULL, -1,
396 int llapi_search_rootpath(char *pathname, const char *fsname)
402 * pathname can be used as an argument by get_root_path(),
403 * clear it for safety
406 return get_root_path(WANT_PATH, (char *)fsname, NULL, pathname, -1,
410 int llapi_search_rootpath_by_dev(char *pathname, dev_t dev)
416 * pathname can be used as an argument by get_root_path(),
417 * clear it for safety
420 return get_root_path(WANT_PATH, NULL, NULL, pathname, -1, &dev, NULL);