Whamcloud - gitweb
LU-11380 llapi: add llapi_fid_parse() helper
[fs/lustre-release.git] / lustre / utils / liblustreapi_fid.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_fid.c
21  *
22  * lustreapi library for FID mapping calls for determining the pathname
23  * of Lustre files from the File IDentifier.
24  *
25  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
26  * Use is subject to license terms.
27  *
28  * Copyright (c) 2011, 2017, Intel Corporation.
29  *
30  * Copyright (c) 2018, 2019, Data Direct Networks
31  */
32
33 /* for O_DIRECTORY */
34 #ifndef _GNU_SOURCE
35 #define _GNU_SOURCE
36 #endif
37
38 #include <ctype.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/ioctl.h>
44 #include <sys/xattr.h>
45 #include <unistd.h>
46
47 #include <libcfs/util/ioctl.h>
48 #include <lustre/lustreapi.h>
49 #include <linux/lustre/lustre_fid.h>
50 #include "lustreapi_internal.h"
51
52 /* strip instances of // (DNE striped directory) when copying to reply buffer */
53 static int copy_strip_dne_path(const char *src, char *tgt, size_t tgtlen)
54 {
55         const char *a;
56         char *b;
57
58         for (a = src, b = tgt; *a != '\0' && b - tgt < tgtlen; a++) {
59                 if (*a == '/' && *(a + 1) == '/')
60                         continue;
61                 *b = *a;
62                 b++;
63         }
64         if (b - tgt >= tgtlen) {
65                 errno = ERANGE;
66                 return -errno;
67         }
68
69         *b = '\0';
70
71         if (tgt[0] == '\0') { /* ROOT path */
72                 tgt[0] = '/';
73                 tgt[1] = '\0';
74         }
75
76         return 0;
77 }
78
79 /**
80  * parse a FID from a string into a binary lu_fid
81  *
82  * Only the format of the FID is checked, not whether the numeric value
83  * contains a valid FID sequence or object ID or version. Optional leading
84  * whitespace and '[' from the standard FID format are skipped.
85  *
86  * \param[in] fidstr    string to be parsed
87  * \param[out] fid      Lustre File IDentifier
88  * \param[out] endptr   pointer to first invalid/unused character in @fidstr
89  *
90  * \retval      0 on success
91  * \retval      -errno on failure
92  */
93 int llapi_fid_parse(const char *fidstr, struct lu_fid *fid, char **endptr)
94 {
95         unsigned long long val;
96         bool bracket = false;
97         char *end = (char *)fidstr;
98         int rc = 0;
99
100         if (!fidstr || !fid) {
101                 rc = -EINVAL;
102                 goto out;
103         }
104
105         while (isspace(*fidstr))
106                 fidstr++;
107         while (*fidstr == '[') {
108                 bracket = true;
109                 fidstr++;
110         }
111
112         /* Parse the FID fields individually with strtoull() instead of a
113          * single call to sscanf() so that the character after the FID can
114          * be returned in @endptr, in case the string has more to parse.
115          * If values are present, but too large for the field, continue
116          * parsing to consume the whole FID and return -ERANGE at the end.
117          */
118         errno = 0;
119         val = strtoull(fidstr, &end, 0);
120         if ((val == 0 && errno == EINVAL) || *end != ':') {
121                 rc = -EINVAL;
122                 goto out;
123         }
124         if (val >= UINT64_MAX)
125                 rc = -ERANGE;
126         else
127                 fid->f_seq = val;
128
129         fidstr = end + 1; /* skip first ':', checked above */
130         errno = 0;
131         val = strtoull(fidstr, &end, 0);
132         if ((val == 0 && errno == EINVAL) || *end != ':') {
133                 rc = -EINVAL;
134                 goto out;
135         }
136         if (val > UINT32_MAX)
137                 rc = -ERANGE;
138         else
139                 fid->f_oid = val;
140
141         fidstr = end + 1; /* skip second ':', checked above */
142         errno = 0;
143         val = strtoull(fidstr, &end, 0);
144         if (val == 0 && errno == EINVAL) {
145                 rc = -EINVAL;
146                 goto out;
147         }
148         if (val > UINT32_MAX)
149                 rc = -ERANGE;
150         else
151                 fid->f_ver = val;
152
153         if (bracket && *end == ']')
154                 end++;
155 out:
156         if (endptr)
157                 *endptr = end;
158
159         errno = -rc;
160         return rc;
161 }
162
163 /* Print mdtname 'name' into 'buf' using 'format'.  Add -MDT0000 if needed.
164  * format must have %s%s, buf must be > 16
165  * Eg: if name = "lustre-MDT0000", "lustre", or "lustre-MDT0000_UUID"
166  *     then buf = "lustre-MDT0000"
167  */
168 static int get_mdtname(char *name, char *format, char *buf)
169 {
170         char suffix[] = "-MDT0000";
171         int len = strlen(name);
172
173         if (len > 5 && strncmp(name + len - 5, "_UUID", 5) == 0) {
174                 name[len - 5] = '\0';
175                 len -= 5;
176         }
177
178         if (len > 8) {
179                 if ((len <= 16) && strncmp(name + len - 8, "-MDT", 4) == 0) {
180                         suffix[0] = '\0';
181                 } else {
182                         /* Not enough room to add suffix */
183                         llapi_err_noerrno(LLAPI_MSG_ERROR,
184                                           "Invalid MDT name |%s|", name);
185                         return -EINVAL;
186                 }
187         }
188
189         return sprintf(buf, format, name, suffix);
190 }
191
192 /** ioctl on filsystem root, with mdtindex sent as data
193  * \param mdtname path, fsname, or mdtname (lutre-MDT0004)
194  * \param mdtidxp pointer to integer within data to be filled in with the
195  *    mdt index (0 if no mdt is specified).  NULL won't be filled.
196  */
197 int root_ioctl(const char *mdtname, int opc, void *data, int *mdtidxp,
198                int want_error)
199 {
200         char fsname[20];
201         char *ptr;
202         int fd, rc;
203         long index;
204
205         /* Take path, fsname, or MDTname.  Assume MDT0000 in former cases.
206          * Open root and parse mdt index.
207          */
208         if (mdtname[0] == '/') {
209                 index = 0;
210                 rc = get_root_path(WANT_FD | want_error, NULL, &fd,
211                                    (char *)mdtname, -1);
212         } else {
213                 if (get_mdtname((char *)mdtname, "%s%s", fsname) < 0)
214                         return -EINVAL;
215                 ptr = fsname + strlen(fsname) - 8;
216                 *ptr = '\0';
217                 index = strtol(ptr + 4, NULL, 16);
218                 rc = get_root_path(WANT_FD | want_error, fsname, &fd, NULL, -1);
219         }
220         if (rc < 0) {
221                 if (want_error)
222                         llapi_err_noerrno(LLAPI_MSG_ERROR,
223                                           "Can't open %s: %d\n", mdtname, rc);
224                 return rc;
225         }
226
227         if (mdtidxp)
228                 *mdtidxp = index;
229
230         rc = ioctl(fd, opc, data);
231         if (rc == -1)
232                 rc = -errno;
233         else
234                 rc = 0;
235         close(fd);
236         return rc;
237 }
238
239 int llapi_fid2path(const char *device, const char *fidstr, char *path,
240                    int pathlen, long long *recno, int *linkno)
241 {
242         struct lu_fid fid;
243         struct getinfo_fid2path *gf;
244         int rc;
245
246         if (!path || pathlen <= 1) {
247                 rc = -EINVAL;
248                 goto out;
249         }
250
251         rc = llapi_fid_parse(fidstr, &fid, NULL);
252         if (!rc && !fid_is_sane(&fid)) {
253                 rc = -EINVAL;
254                 goto out;
255         }
256         if (rc) {
257                 llapi_err_noerrno(LLAPI_MSG_ERROR,
258                                   "bad FID format '%s', should be [seq:oid:ver] (e.g. "DFID")\n",
259                                   fidstr,
260                                   (unsigned long long)FID_SEQ_NORMAL, 2, 0);
261                 goto out;
262         }
263
264         gf = malloc(sizeof(*gf) + pathlen);
265         if (gf == NULL) {
266                 rc = -ENOMEM;
267                 goto out;
268         }
269
270         gf->gf_fid = fid;
271         if (recno)
272                 gf->gf_recno = *recno;
273         if (linkno)
274                 gf->gf_linkno = *linkno;
275         gf->gf_pathlen = pathlen;
276
277         /* Take path or fsname */
278         rc = root_ioctl(device, OBD_IOC_FID2PATH, gf, NULL, 0);
279         if (rc)
280                 goto out_free;
281
282         rc = copy_strip_dne_path(gf->gf_u.gf_path, path, pathlen);
283
284         if (recno)
285                 *recno = gf->gf_recno;
286         if (linkno)
287                 *linkno = gf->gf_linkno;
288
289 out_free:
290         free(gf);
291 out:
292         errno = -rc;
293         return rc;
294 }
295
296 static int fid_from_lma(const char *path, int fd, struct lu_fid *fid)
297 {
298         char buf[512];
299         struct lustre_mdt_attrs *lma;
300         int rc = -1;
301
302         if (fd >= 0)
303                 rc = fgetxattr(fd, XATTR_NAME_LMA, buf, sizeof(buf));
304         else if (path)
305                 rc = lgetxattr(path, XATTR_NAME_LMA, buf, sizeof(buf));
306         else
307                 errno = EINVAL;
308         if (rc < 0)
309                 return -errno;
310
311         lma = (struct lustre_mdt_attrs *)buf;
312         fid_le_to_cpu(fid, &lma->lma_self_fid);
313
314         return 0;
315 }
316
317 int llapi_get_mdt_index_by_fid(int fd, const struct lu_fid *fid,
318                                int *mdt_index)
319 {
320         int rc;
321
322         rc = ioctl(fd, LL_IOC_FID2MDTIDX, fid);
323         if (rc < 0)
324                 return -errno;
325
326         if (mdt_index)
327                 *mdt_index = rc;
328
329         return rc;
330 }
331
332 int llapi_fd2fid(int fd, struct lu_fid *fid)
333 {
334         int rc;
335
336         memset(fid, 0, sizeof(*fid));
337
338         rc = ioctl(fd, LL_IOC_PATH2FID, fid) < 0 ? -errno : 0;
339         /* Allow extracting the FID from an ldiskfs-mounted filesystem */
340         if (rc < 0) {
341                 if (errno == EINVAL || errno == ENOTTY)
342                         rc = fid_from_lma(NULL, fd, fid);
343                 else
344                         rc = -errno;
345         }
346
347         return rc;
348 }
349
350 int llapi_path2fid(const char *path, struct lu_fid *fid)
351 {
352         int fd, rc;
353
354         fd = open(path, O_RDONLY | O_NONBLOCK | O_NOFOLLOW);
355         if (fd >= 0) {
356                 rc = llapi_fd2fid(fd, fid);
357                 close(fd);
358         } else {
359                 memset(fid, 0, sizeof(*fid));
360                 rc = -errno;
361         }
362
363         if (rc == -ELOOP || rc == -ENXIO || rc == -EINVAL || rc == -ENOTTY)
364                 rc = fid_from_lma(path, -1, fid);
365
366         return rc;
367 }
368
369 int llapi_fd2parent(int fd, unsigned int linkno, struct lu_fid *parent_fid,
370                     char *name, size_t name_size)
371 {
372         struct getparent *gp;
373         int rc;
374
375         if (name && name_size <= 1) {
376                 errno = EOVERFLOW;
377                 return -errno;
378         }
379
380         gp = malloc(sizeof(*gp) + name_size);
381         if (gp == NULL) {
382                 errno = ENOMEM;
383                 return -errno;
384         }
385
386         gp->gp_linkno = linkno;
387         gp->gp_name_size = name_size;
388
389         rc = ioctl(fd, LL_IOC_GETPARENT, gp);
390         if (rc < 0) {
391                 rc = -errno;
392                 goto err_free;
393         }
394
395         if (parent_fid)
396                 *parent_fid = gp->gp_fid;
397
398         if (name)
399                 rc = copy_strip_dne_path(gp->gp_name, name, name_size);
400
401 err_free:
402         free(gp);
403         return rc;
404 }
405
406 int llapi_path2parent(const char *path, unsigned int linkno,
407                       struct lu_fid *parent_fid, char *name, size_t name_size)
408 {
409         int fd;
410         int rc;
411
412         fd = open(path, O_RDONLY | O_NONBLOCK | O_NOFOLLOW);
413         if (fd < 0)
414                 return -errno;
415
416         rc = llapi_fd2parent(fd, linkno, parent_fid, name, name_size);
417         close(fd);
418
419         return rc;
420 }
421
422 /**
423  * Attempt to open a file with Lustre file identifier \a fid
424  * and return an open file descriptor.
425  *
426  * \param[in] lustre_dir        path within Lustre filesystem containing \a fid
427  * \param[in] fid               Lustre file identifier of file to open
428  * \param[in] flags             open() flags
429  *
430  * \retval                      non-negative file descriptor on successful open
431  * \retval                      negative errno if an error occurred
432  */
433 int llapi_open_by_fid(const char *lustre_dir, const struct lu_fid *fid,
434                       int flags)
435 {
436         char mntdir[PATH_MAX];
437         char path[PATH_MAX + 64];
438         int rc;
439
440         rc = llapi_search_mounts(lustre_dir, 0, mntdir, NULL);
441         if (rc)
442                 return rc;
443
444         snprintf(path, sizeof(path), "%s/.lustre/fid/"DFID, mntdir, PFID(fid));
445         rc = open(path, flags);
446         if (rc < 0)
447                 rc = -errno;
448
449         return rc;
450 }