Whamcloud - gitweb
b=16098
[fs/lustre-release.git] / lustre / utils / liblustreapi.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright  2008 Sun Microsystems, Inc. All rights reserved
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lustre/utils/liblustreapi.c
37  *
38  * Author: Peter J. Braam <braam@clusterfs.com>
39  * Author: Phil Schwan <phil@clusterfs.com>
40  * Author: Robert Read <rread@clusterfs.com>
41  */
42
43 /* for O_DIRECTORY */
44 #ifndef _GNU_SOURCE
45 #define _GNU_SOURCE
46 #endif
47
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <stddef.h>
52 #include <sys/ioctl.h>
53 #include <unistd.h>
54 #include <fcntl.h>
55 #include <errno.h>
56 #include <dirent.h>
57 #include <stdarg.h>
58 #include <sys/stat.h>
59 #include <sys/types.h>
60 #include <sys/syscall.h>
61 #include <fnmatch.h>
62 #ifdef HAVE_LINUX_UNISTD_H
63 #include <linux/unistd.h>
64 #else
65 #include <unistd.h>
66 #endif
67
68 #include <liblustre.h>
69 #include <lnet/lnetctl.h>
70 #include <obd.h>
71 #include <lustre_lib.h>
72 #include <obd_lov.h>
73 #include <lustre/liblustreapi.h>
74
75 static unsigned llapi_dir_filetype_table[] = {
76         [DT_UNKNOWN]= 0,
77         [DT_FIFO]= S_IFIFO,
78         [DT_CHR] = S_IFCHR,
79         [DT_DIR] = S_IFDIR,
80         [DT_BLK] = S_IFBLK,
81         [DT_REG] = S_IFREG,
82         [DT_LNK] = S_IFLNK,
83         [DT_SOCK]= S_IFSOCK,
84 #if defined(DT_DOOR) && defined(S_IFDOOR)
85         [DT_DOOR]= S_IFDOOR,
86 #endif
87 };
88
89 #if defined(DT_DOOR) && defined(S_IFDOOR)
90 static const int DT_MAX = DT_DOOR;
91 #else
92 static const int DT_MAX = DT_SOCK;
93 #endif
94
95 static unsigned llapi_filetype_dir_table[] = {
96         [0]= DT_UNKNOWN,
97         [S_IFIFO]= DT_FIFO,
98         [S_IFCHR] = DT_CHR,
99         [S_IFDIR] = DT_DIR,
100         [S_IFBLK] = DT_BLK,
101         [S_IFREG] = DT_REG,
102         [S_IFLNK] = DT_LNK,
103         [S_IFSOCK]= DT_SOCK,
104 #if defined(DT_DOOR) && defined(S_IFDOOR)
105         [S_IFDOOR]= DT_DOOR,
106 #endif
107 };
108
109 #if defined(DT_DOOR) && defined(S_IFDOOR)
110 static const int S_IFMAX = DT_DOOR;
111 #else
112 static const int S_IFMAX = DT_SOCK;
113 #endif
114
115 /* liblustreapi message level */
116 static int llapi_msg_level = LLAPI_MSG_MAX;
117
118 void llapi_msg_set_level(int level)
119 {
120         /* ensure level is in the good range */
121         if (level < LLAPI_MSG_OFF)
122                 llapi_msg_level = LLAPI_MSG_OFF;
123         else if (level > LLAPI_MSG_MAX)
124                 llapi_msg_level = LLAPI_MSG_MAX;
125         else
126                 llapi_msg_level = level;
127 }
128
129 void llapi_err(int level, char *fmt, ...)
130 {
131         va_list args;
132         int tmp_errno = abs(errno);
133
134         if ((level & LLAPI_MSG_MASK) > llapi_msg_level)
135                 return;
136
137         va_start(args, fmt);
138         vfprintf(stderr, fmt, args);
139         va_end(args);
140
141         if (level & LLAPI_MSG_NO_ERRNO)
142                 fprintf(stderr, "\n");
143         else
144                 fprintf(stderr, ": %s (%d)\n", strerror(tmp_errno), tmp_errno);
145 }
146
147 #define llapi_err_noerrno(level, fmt, a...)                             \
148         llapi_err((level) | LLAPI_MSG_NO_ERRNO, fmt, ## a)
149
150 void llapi_printf(int level, char *fmt, ...)
151 {
152         va_list args;
153
154         if ((level & LLAPI_MSG_MASK) > llapi_msg_level)
155                 return;
156
157         va_start(args, fmt);
158         vfprintf(stdout, fmt, args);
159         va_end(args);
160 }
161
162 int parse_size(char *optarg, unsigned long long *size,
163                unsigned long long *size_units)
164 {
165         char *end;
166
167         *size = strtoul(optarg, &end, 0);
168
169         if (*end != '\0') {
170                 if ((*end == 'b') && *(end+1) == '\0' &&
171                     (*size & (~0ULL << (64 - 9))) == 0) {
172                         *size <<= 9;
173                         *size_units = 1 << 9;
174                 } else if ((*end == 'k' || *end == 'K') &&
175                            *(end+1) == '\0' && (*size &
176                            (~0ULL << (64 - 10))) == 0) {
177                         *size <<= 10;
178                         *size_units = 1 << 10;
179                 } else if ((*end == 'm' || *end == 'M') &&
180                            *(end+1) == '\0' && (*size &
181                            (~0ULL << (64 - 20))) == 0) {
182                         *size <<= 20;
183                         *size_units = 1 << 20;
184                 } else if ((*end == 'g' || *end == 'G') &&
185                            *(end+1) == '\0' && (*size &
186                            (~0ULL << (64 - 30))) == 0) {
187                         *size <<= 30;
188                         *size_units = 1 << 30;
189                 } else if ((*end == 't' || *end == 'T') &&
190                            *(end+1) == '\0' && (*size &
191                            (~0ULL << (64 - 40))) == 0) {
192                         *size <<= 40;
193                         *size_units = 1ULL << 40;
194                 } else if ((*end == 'p' || *end == 'P') &&
195                            *(end+1) == '\0' && (*size &
196                            (~0ULL << (64 - 50))) == 0) {
197                         *size <<= 50;
198                         *size_units = 1ULL << 50;
199                 } else if ((*end == 'e' || *end == 'E') &&
200                            *(end+1) == '\0' && (*size &
201                            (~0ULL << (64 - 60))) == 0) {
202                         *size <<= 60;
203                         *size_units = 1ULL << 60;
204                 } else {
205                         return -1;
206                 }
207         }
208
209         return 0;
210 }
211
212 int llapi_file_open(const char *name, int flags, int mode,
213                     unsigned long stripe_size, int stripe_offset,
214                     int stripe_count, int stripe_pattern)
215 {
216         struct lov_user_md lum = { 0 };
217         int fd, rc = 0;
218         int isdir = 0;
219         int page_size;
220
221         fd = open(name, flags | O_LOV_DELAY_CREATE, mode);
222         if (fd < 0 && errno == EISDIR) {
223                 fd = open(name, O_DIRECTORY | O_RDONLY);
224                 isdir++;
225         }
226
227         if (fd < 0) {
228                 rc = -errno;
229                 llapi_err(LLAPI_MSG_ERROR, "unable to open '%s'", name);
230                 return rc;
231         }
232
233         /* 64 KB is the largest common page size I'm aware of (on ia64), but
234          * check the local page size just in case. */
235         page_size = LOV_MIN_STRIPE_SIZE;
236         if (getpagesize() > page_size) {
237                 page_size = getpagesize();
238                 llapi_err_noerrno(LLAPI_MSG_WARN, 
239                                   "warning: your page size (%u) is "
240                                   "larger than expected (%u)", page_size, 
241                                   LOV_MIN_STRIPE_SIZE);
242         }
243         if (stripe_size < 0 || (stripe_size & (LOV_MIN_STRIPE_SIZE - 1))) {
244                 errno = rc = -EINVAL;
245                 llapi_err(LLAPI_MSG_ERROR, "error: bad stripe_size %lu, "
246                           "must be an even multiple of %d bytes", 
247                           stripe_size, page_size);
248                 goto out;
249         }
250         if (stripe_offset < -1 || stripe_offset > MAX_OBD_DEVICES) {
251                 errno = rc = -EINVAL;
252                 llapi_err(LLAPI_MSG_ERROR, "error: bad stripe offset %d", 
253                           stripe_offset);
254                 goto out;
255         }
256         if (stripe_count < -1 || stripe_count > LOV_MAX_STRIPE_COUNT) {
257                 errno = rc = -EINVAL;
258                 llapi_err(LLAPI_MSG_ERROR, "error: bad stripe count %d", 
259                           stripe_count);
260                 goto out;
261         }
262         if (stripe_count > 0 && (__u64)stripe_size * stripe_count > 0xffffffff){
263                 errno = rc = -EINVAL;
264                 llapi_err(LLAPI_MSG_ERROR, "error: stripe_size %lu * "
265                           "stripe_count %u exceeds 4GB", stripe_size, 
266                           stripe_count);
267                 goto out;
268         }
269
270         /*  Initialize IOCTL striping pattern structure */
271         lum.lmm_magic = LOV_USER_MAGIC;
272         lum.lmm_pattern = stripe_pattern;
273         lum.lmm_stripe_size = stripe_size;
274         lum.lmm_stripe_count = stripe_count;
275         lum.lmm_stripe_offset = stripe_offset;
276
277         if (ioctl(fd, LL_IOC_LOV_SETSTRIPE, &lum)) {
278                 char *errmsg = "stripe already set";
279                 rc = -errno;
280                 if (errno != EEXIST && errno != EALREADY)
281                         errmsg = strerror(errno);
282
283                 llapi_err_noerrno(LLAPI_MSG_ERROR,
284                                   "error on ioctl "LPX64" for '%s' (%d): %s",
285                                   (__u64)LL_IOC_LOV_SETSTRIPE, name, fd, errmsg);
286         }
287 out:
288         if (rc) {
289                 close(fd);
290                 fd = rc;
291         }
292
293         return fd;
294 }
295
296 int llapi_file_create(const char *name, unsigned long stripe_size,
297                       int stripe_offset, int stripe_count, int stripe_pattern)
298 {
299         int fd;
300
301         fd = llapi_file_open(name, O_CREAT | O_WRONLY, 0644, stripe_size,
302                              stripe_offset, stripe_count, stripe_pattern);
303         if (fd < 0)
304                 return fd;
305
306         close(fd);
307         return 0;
308 }
309
310 typedef int (semantic_func_t)(char *path, DIR *parent, DIR *d,
311                               void *data, cfs_dirent_t *de);
312
313 #define MAX_LOV_UUID_COUNT      max(LOV_MAX_STRIPE_COUNT, 1000)
314 #define OBD_NOT_FOUND           (-1)
315
316 static int common_param_init(struct find_param *param)
317 {
318         param->lumlen = lov_mds_md_size(MAX_LOV_UUID_COUNT);
319         if ((param->lmd = malloc(sizeof(lstat_t) + param->lumlen)) == NULL) {
320                 llapi_err(LLAPI_MSG_ERROR, 
321                           "error: allocation of %d bytes for ioctl",
322                           sizeof(lstat_t) + param->lumlen);
323                 return -ENOMEM;
324         }
325
326         param->got_uuids = 0;
327         param->obdindexes = NULL;
328         param->obdindex = OBD_NOT_FOUND;
329         return 0;
330 }
331
332 static void find_param_fini(struct find_param *param)
333 {
334         if (param->obdindexes)
335                 free(param->obdindexes);
336
337         if (param->lmd)
338                 free(param->lmd);
339 }
340
341 int llapi_file_get_lov_fuuid(int fd, struct obd_uuid *lov_name)
342 {
343         int rc = ioctl(fd, OBD_IOC_GETNAME, lov_name);
344         if (rc) {
345                 rc = errno;
346                 llapi_err(LLAPI_MSG_ERROR, "error: can't get lov name.");
347         }
348         return rc;
349 }
350
351 int llapi_file_get_lov_uuid(const char *path, struct obd_uuid *lov_uuid)
352 {
353         int fd, rc;
354
355         fd = open(path, O_RDONLY);
356         if (fd < 0) {
357                 rc = errno;
358                 llapi_err(LLAPI_MSG_ERROR, "error opening %s\n", path);
359                 return rc;
360         }
361
362         rc = llapi_file_get_lov_fuuid(fd, lov_uuid);
363
364         close(fd);
365
366         return rc;
367 }
368
369 /*
370  * If uuidp is NULL, return the number of available obd uuids.
371  * If uuidp is non-NULL, then it will return the uuids of the obds. If
372  * there are more OSTs then allocated to uuidp, then an error is returned with
373  * the ost_count set to number of available obd uuids.
374  */
375 int llapi_lov_get_uuids(int fd, struct obd_uuid *uuidp, int *ost_count)
376 {
377         struct obd_uuid lov_name;
378         char buf[1024];
379         FILE *fp;
380         int rc = 0, index = 0;
381
382         /* Get the lov name */
383         rc = llapi_file_get_lov_fuuid(fd, &lov_name);
384         if (rc)
385                 return rc;
386
387         /* Now get the ost uuids from /proc */
388         snprintf(buf, sizeof(buf), "/proc/fs/lustre/lov/%s/target_obd",
389                  lov_name.uuid);
390         fp = fopen(buf, "r");
391         if (fp == NULL) {
392                 rc = errno;
393                 llapi_err(LLAPI_MSG_ERROR, "error: opening '%s'", buf);
394                 return rc;
395         }
396
397         while (fgets(buf, sizeof(buf), fp) != NULL) {
398                 if (uuidp && (index < *ost_count)) {
399                         if (sscanf(buf, "%d: %s", &index, uuidp[index].uuid) <2)
400                                 break;
401                 }
402                 index++;
403         }
404
405         fclose(fp);
406
407         if (uuidp && (index >= *ost_count))
408                 return -EOVERFLOW;
409
410         *ost_count = index;
411         return rc;
412 }
413
414 /* Here, param->obduuid points to a single obduuid, the index of which is
415  * returned in param->obdindex */
416 static int setup_obd_uuid(DIR *dir, char *dname, struct find_param *param)
417 {
418         struct obd_uuid lov_uuid;
419         char uuid[sizeof(struct obd_uuid)];
420         char buf[1024];
421         FILE *fp;
422         int rc = 0, index;
423
424         /* Get the lov name */
425         rc = llapi_file_get_lov_fuuid(dirfd(dir), &lov_uuid);
426         if (rc) {
427                 if (errno != ENOTTY) {
428                         rc = errno;
429                         llapi_err(LLAPI_MSG_ERROR, 
430                                   "error: can't get lov name: %s", dname);
431                 } else {
432                         rc = 0;
433                 }
434                 return rc;
435         }
436
437         param->got_uuids = 1;
438
439         /* Now get the ost uuids from /proc */
440         snprintf(buf, sizeof(buf), "/proc/fs/lustre/lov/%s/target_obd",
441                  lov_uuid.uuid);
442         fp = fopen(buf, "r");
443         if (fp == NULL) {
444                 rc = errno;
445                 llapi_err(LLAPI_MSG_ERROR, "error: opening '%s'", buf);
446                 return rc;
447         }
448
449         if (!param->obduuid && !param->quiet && !param->obds_printed)
450                 llapi_printf(LLAPI_MSG_NORMAL, "OBDS:\n");
451
452         while (fgets(buf, sizeof(buf), fp) != NULL) {
453                 if (sscanf(buf, "%d: %s", &index, uuid) < 2)
454                         break;
455
456                 if (param->obduuid) {
457                         if (strncmp(param->obduuid->uuid, uuid,
458                                     sizeof(uuid)) == 0) {
459                                 param->obdindex = index;
460                                 break;
461                         }
462                 } else if (!param->quiet && !param->obds_printed) {
463                         /* Print everything */
464                         llapi_printf(LLAPI_MSG_NORMAL, "%s", buf);
465                 }
466         }
467         param->obds_printed = 1;
468
469         fclose(fp);
470
471         if (!param->quiet && param->obduuid &&
472             (param->obdindex == OBD_NOT_FOUND)) {
473                 llapi_err_noerrno(LLAPI_MSG_ERROR, 
474                                   "error: %s: unknown obduuid: %s",
475                                   __FUNCTION__, param->obduuid->uuid);
476                 //rc = EINVAL;
477         }
478
479         return (rc);
480 }
481
482 /* In this case, param->obduuid will be an array of obduuids and
483  * obd index for all these obduuids will be returned in
484  * param->obdindexes */
485 static int setup_obd_indexes(DIR *dir, struct find_param *param)
486 {
487         struct obd_uuid *uuids = NULL;
488         int obdcount = INIT_ALLOC_NUM_OSTS;
489         int ret, obd_valid = 0, obdnum, i;
490
491         uuids = (struct obd_uuid *)malloc(INIT_ALLOC_NUM_OSTS *
492                                           sizeof(struct obd_uuid));
493         if (uuids == NULL)
494                 return -ENOMEM;
495
496 retry_get_uuids:
497         ret = llapi_lov_get_uuids(dirfd(dir), uuids,
498                                   &obdcount);
499         if (ret) {
500                 struct obd_uuid *uuids_temp;
501
502                 if (ret == -EOVERFLOW) {
503                         uuids_temp = realloc(uuids, obdcount *
504                                              sizeof(struct obd_uuid));
505                         if (uuids_temp != NULL)
506                                 goto retry_get_uuids;
507                         else
508                                 ret = -ENOMEM;
509                 }
510
511                 llapi_err(LLAPI_MSG_ERROR, "get ost uuid failed");
512                 return ret;
513         }
514
515         param->obdindexes = malloc(param->num_obds * sizeof(param->obdindex));
516         if (param->obdindexes == NULL)
517                 return -ENOMEM;
518
519         for (obdnum = 0; obdnum < param->num_obds; obdnum++) {
520                 for (i = 0; i <= obdcount; i++) {
521                         if (strcmp((char *)&param->obduuid[obdnum].uuid,
522                                    (char *)&uuids[i]) == 0) {
523                                 param->obdindexes[obdnum] = i;
524                                 obd_valid++;
525                                 break;
526                         }
527                 }
528                 if (i == obdcount)
529                         param->obdindexes[obdnum] = OBD_NOT_FOUND;
530         }
531
532         if (obd_valid == 0)
533                 param->obdindex = OBD_NOT_FOUND;
534         else
535                 param->obdindex = obd_valid;
536
537         param->got_uuids = 1;
538
539         return 0;
540 }
541
542 void lov_dump_user_lmm_v1(struct lov_user_md_v1 *lum, char *path, int is_dir,
543                           int obdindex, int quiet, int header, int body)
544 {
545         int i, obdstripe = 0;
546
547         if (obdindex != OBD_NOT_FOUND) {
548                 for (i = 0; !is_dir && i < lum->lmm_stripe_count; i++) {
549                         if (obdindex == lum->lmm_objects[i].l_ost_idx) {
550                                 llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
551                                 obdstripe = 1;
552                                 break;
553                         }
554                 }
555         } else if (!quiet) {
556                 llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
557                 obdstripe = 1;
558         }
559
560         /* if it's a directory */
561         if (is_dir) {
562                 if (obdstripe == 1) {
563                         if (lum->lmm_object_gr == LOV_OBJECT_GROUP_DEFAULT) {
564                                 llapi_printf(LLAPI_MSG_NORMAL, "(Default) ");
565                                 lum->lmm_object_gr = LOV_OBJECT_GROUP_CLEAR;
566                         }
567                         llapi_printf(LLAPI_MSG_NORMAL, 
568                                      "stripe_count: %d stripe_size: %u "
569                                      "stripe_offset: %d\n",
570                                      lum->lmm_stripe_count == (__u16)-1 ? -1 :
571                                      lum->lmm_stripe_count,
572                                      lum->lmm_stripe_size,
573                                      lum->lmm_stripe_offset == (__u16)-1 ? -1 :
574                                      lum->lmm_stripe_offset);
575                 }
576                 return;
577         }
578
579         if (header && (obdstripe == 1)) {
580                 llapi_printf(LLAPI_MSG_NORMAL, 
581                              "lmm_magic:          0x%08X\n",  lum->lmm_magic);
582                 llapi_printf(LLAPI_MSG_NORMAL, 
583                              "lmm_object_gr:      "LPX64"\n", lum->lmm_object_gr);
584                 llapi_printf(LLAPI_MSG_NORMAL, 
585                              "lmm_object_id:      "LPX64"\n", lum->lmm_object_id);
586                 llapi_printf(LLAPI_MSG_NORMAL, 
587                              "lmm_stripe_count:   %u\n", (int)lum->lmm_stripe_count);
588                 llapi_printf(LLAPI_MSG_NORMAL, 
589                              "lmm_stripe_size:    %u\n",      lum->lmm_stripe_size);
590                 llapi_printf(LLAPI_MSG_NORMAL, 
591                              "lmm_stripe_pattern: %x\n",      lum->lmm_pattern);
592         }
593
594         if (body) {
595                 if ((!quiet) && (obdstripe == 1))
596                         llapi_printf(LLAPI_MSG_NORMAL, 
597                                      "\tobdidx\t\t objid\t\tobjid\t\t group\n");
598
599                 for (i = 0; i < lum->lmm_stripe_count; i++) {
600                         int idx = lum->lmm_objects[i].l_ost_idx;
601                         long long oid = lum->lmm_objects[i].l_object_id;
602                         long long gr = lum->lmm_objects[i].l_object_gr;
603                         if ((obdindex == OBD_NOT_FOUND) || (obdindex == idx))
604                                 llapi_printf(LLAPI_MSG_NORMAL, 
605                                              "\t%6u\t%14llu\t%#13llx\t%14llu%s\n",
606                                              idx, oid, oid, gr,
607                                              obdindex == idx ? " *" : "");
608                 }
609                 llapi_printf(LLAPI_MSG_NORMAL, "\n");
610         }
611 }
612
613 void lov_dump_user_lmm_join(struct lov_user_md_v1 *lum, char *path,
614                             int is_dir, int obdindex, int quiet,
615                             int header, int body)
616 {
617         struct lov_user_md_join *lumj = (struct lov_user_md_join *)lum;
618         int i, obdstripe = 0;
619
620         if (obdindex != OBD_NOT_FOUND) {
621                 for (i = 0; i < lumj->lmm_stripe_count; i++) {
622                         if (obdindex == lumj->lmm_objects[i].l_ost_idx) {
623                                 llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
624                                 obdstripe = 1;
625                                 break;
626                         }
627                 }
628         } else if (!quiet) {
629                 llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
630                 obdstripe = 1;
631         }
632
633         if (header && obdstripe == 1) {
634                 llapi_printf(LLAPI_MSG_NORMAL, "lmm_magic:          0x%08X\n",  
635                              lumj->lmm_magic);
636                 llapi_printf(LLAPI_MSG_NORMAL, "lmm_object_gr:      "LPX64"\n", 
637                              lumj->lmm_object_gr);
638                 llapi_printf(LLAPI_MSG_NORMAL, "lmm_object_id:      "LPX64"\n", 
639                              lumj->lmm_object_id);
640                 llapi_printf(LLAPI_MSG_NORMAL, "lmm_stripe_count:   %u\n", 
641                              (int)lumj->lmm_stripe_count);
642                 llapi_printf(LLAPI_MSG_NORMAL, "lmm_stripe_size:    %u\n",
643                              lumj->lmm_stripe_size);
644                 llapi_printf(LLAPI_MSG_NORMAL, "lmm_stripe_pattern: %x\n",
645                              lumj->lmm_pattern);
646                 llapi_printf(LLAPI_MSG_NORMAL, "lmm_extent_count:   %x\n",
647                              lumj->lmm_extent_count);
648         }
649
650         if (body) {
651                 unsigned long long start = -1, end = 0;
652                 if (!quiet && obdstripe == 1)
653                         llapi_printf(LLAPI_MSG_NORMAL, 
654                                      "joined\tobdidx\t\t objid\t\tobjid\t\t group"
655                                      "\t\tstart\t\tend\n");
656                 for (i = 0; i < lumj->lmm_stripe_count; i++) {
657                         int idx = lumj->lmm_objects[i].l_ost_idx;
658                         long long oid = lumj->lmm_objects[i].l_object_id;
659                         long long gr = lumj->lmm_objects[i].l_object_gr;
660                         if (obdindex == OBD_NOT_FOUND || obdindex == idx)
661                                 llapi_printf(LLAPI_MSG_NORMAL, 
662                                              "\t%6u\t%14llu\t%#13llx\t%14llu%s",
663                                              idx, oid, oid, gr,
664                                              obdindex == idx ? " *" : "");
665                         if (start != lumj->lmm_objects[i].l_extent_start ||
666                             end != lumj->lmm_objects[i].l_extent_end) {
667                                 start = lumj->lmm_objects[i].l_extent_start;
668                                 llapi_printf(LLAPI_MSG_NORMAL, "\t%14llu", start);
669                                 end = lumj->lmm_objects[i].l_extent_end;
670                                 if (end == (unsigned long long)-1)
671                                         llapi_printf(LLAPI_MSG_NORMAL, 
672                                                      "\t\tEOF\n");
673                                 else
674                                         llapi_printf(LLAPI_MSG_NORMAL, 
675                                                      "\t\t%llu\n", end);
676                         } else {
677                                 llapi_printf(LLAPI_MSG_NORMAL, "\t\t\t\t\n");
678                         }
679                 }
680                 llapi_printf(LLAPI_MSG_NORMAL, "\n");
681         }
682 }
683
684 void llapi_lov_dump_user_lmm(struct find_param *param,
685                              char *path, int is_dir)
686 {
687         switch(*(__u32 *)&param->lmd->lmd_lmm) { /* lum->lmm_magic */
688         case LOV_USER_MAGIC_V1:
689                 lov_dump_user_lmm_v1(&param->lmd->lmd_lmm, path, is_dir,
690                                       param->obdindex, param->quiet,
691                                       param->verbose,
692                                       (param->verbose || !param->obduuid));
693                 break;
694         case LOV_USER_MAGIC_JOIN:
695                 lov_dump_user_lmm_join(&param->lmd->lmd_lmm, path, is_dir,
696                                        param->obdindex, param->quiet,
697                                        param->verbose,
698                                        (param->verbose || !param->obduuid));
699                 break;
700         default:
701                 llapi_printf(LLAPI_MSG_NORMAL, 
702                              "unknown lmm_magic:  %#x (expecting %#x)\n",
703                        *(__u32 *)&param->lmd->lmd_lmm, LOV_USER_MAGIC_V1);
704                 return;
705         }
706 }
707
708 int llapi_file_get_stripe(const char *path, struct lov_user_md *lum)
709 {
710         const char *fname;
711         char *dname;
712         int fd, rc = 0;
713
714         fname = strrchr(path, '/');
715
716         /* It should be a file (or other non-directory) */
717         if (fname == NULL) {
718                 dname = (char *)malloc(2);
719                 if (dname == NULL)
720                         return ENOMEM;
721                 strcpy(dname, ".");
722                 fname = (char *)path;
723         } else {
724                 dname = (char *)malloc(fname - path + 1);
725                 if (dname == NULL)
726                         return ENOMEM;
727                 strncpy(dname, path, fname - path);
728                 dname[fname - path] = '\0';
729                 fname++;
730         }
731
732         if ((fd = open(dname, O_RDONLY)) == -1) {
733                 rc = errno;
734                 free(dname);
735                 return rc;
736         }
737
738         strcpy((char *)lum, fname);
739         if (ioctl(fd, IOC_MDC_GETFILESTRIPE, (void *)lum) == -1)
740                 rc = errno;
741
742         if (close(fd) == -1 && rc == 0)
743                 rc = errno;
744
745         free(dname);
746
747         return rc;
748 }
749
750 int llapi_file_lookup(int dirfd, const char *name)
751 {
752         struct obd_ioctl_data data = { 0 };
753         char rawbuf[8192];
754         char *buf = rawbuf;
755         int rc;
756
757         if (dirfd < 0 || name == NULL)
758                 return -EINVAL;
759
760         data.ioc_version = OBD_IOCTL_VERSION;
761         data.ioc_len = sizeof(data);
762         data.ioc_inlbuf1 = (char *)name;
763         data.ioc_inllen1 = strlen(name) + 1;
764
765         rc = obd_ioctl_pack(&data, &buf, sizeof(rawbuf));
766         if (rc) {
767                 llapi_err(LLAPI_MSG_ERROR,
768                           "error: IOC_MDC_LOOKUP pack failed for '%s': rc %d",
769                           name, rc);
770                 return rc;
771         }
772
773         return ioctl(dirfd, IOC_MDC_LOOKUP, buf);
774 }
775
776 int llapi_mds_getfileinfo(char *path, DIR *parent,
777                           struct lov_user_mds_data *lmd)
778 {
779         lstat_t *st = &lmd->lmd_st;
780         char *fname = strrchr(path, '/');
781         int ret = 0;
782
783         if (parent == NULL)
784                 return -EINVAL;
785
786         fname = (fname == NULL ? path : fname + 1);
787         /* retrieve needed file info */
788         strncpy((char *)lmd, fname, lov_mds_md_size(MAX_LOV_UUID_COUNT));
789         ret = ioctl(dirfd(parent), IOC_MDC_GETFILEINFO, (void *)lmd);
790
791         if (ret) {
792                 if (errno == ENOTTY) {
793                         /* ioctl is not supported, it is not a lustre fs.
794                          * Do the regular lstat(2) instead. */
795                         ret = lstat_f(path, st);
796                         if (ret) {
797                                 llapi_err(LLAPI_MSG_ERROR, 
798                                           "error: %s: lstat failed for %s",
799                                           __FUNCTION__, path);
800                                 return ret;
801                         }
802                 } else if (errno == ENOENT) {
803                         llapi_err(LLAPI_MSG_WARN, 
804                                   "warning: %s: %s does not exist", 
805                                   __FUNCTION__, path);
806                         return -ENOENT;
807                 } else {
808                         llapi_err(LLAPI_MSG_ERROR, 
809                                   "error: %s: IOC_MDC_GETFILEINFO failed for %s",
810                                   __FUNCTION__, path);
811                         return ret;
812                 }
813         }
814
815         return 0;
816 }
817
818 static DIR *opendir_parent(char *path)
819 {
820         DIR *parent;
821         char *fname;
822         char c;
823
824         fname = strrchr(path, '/');
825         if (fname == NULL)
826                 return opendir(".");
827
828         c = fname[1];
829         fname[1] = '\0';
830         parent = opendir(path);
831         fname[1] = c;
832         return parent;
833 }
834
835 static int llapi_semantic_traverse(char *path, int size, DIR *parent,
836                                    semantic_func_t sem_init,
837                                    semantic_func_t sem_fini, void *data,
838                                    cfs_dirent_t *de)
839 {
840         cfs_dirent_t *dent;
841         int len, ret;
842         DIR *d, *p = NULL;
843
844         ret = 0;
845         len = strlen(path);
846
847         d = opendir(path);
848         if (!d && errno != ENOTDIR) {
849                 llapi_err(LLAPI_MSG_ERROR, "%s: Failed to open '%s'",
850                           __FUNCTION__, path);
851                 return -EINVAL;
852         } else if (!d && !parent) {
853                 /* ENOTDIR. Open the parent dir. */
854                 p = opendir_parent(path);
855                 if (!p)
856                         GOTO(out, ret = -EINVAL);
857         }
858
859         if (sem_init && (ret = sem_init(path, parent ?: p, d, data, de)))
860                 goto err;
861
862         if (!d)
863                 GOTO(out, ret = 0);
864
865         while ((dent = readdir64(d)) != NULL) {
866                 ((struct find_param *)data)->have_fileinfo = 0;
867
868                 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
869                         continue;
870
871                 path[len] = 0;
872                 if ((len + dent->d_reclen + 2) > size) {
873                         llapi_err(LLAPI_MSG_ERROR,
874                                   "error: %s: string buffer is too small",
875                                   __FUNCTION__);
876                         break;
877                 }
878                 strcat(path, "/");
879                 strcat(path, dent->d_name);
880
881                 if (dent->d_type == DT_UNKNOWN) {
882                         lstat_t *st = &((struct find_param *)data)->lmd->lmd_st;
883
884                         ret = llapi_mds_getfileinfo(path, d,
885                                              ((struct find_param *)data)->lmd);
886                         if (ret == 0) {
887                                 ((struct find_param *)data)->have_fileinfo = 1;
888                                 dent->d_type = llapi_filetype_dir_table[st->st_mode &
889                                                                         S_IFMT];
890                         }
891                         if (ret == -ENOENT)
892                                 continue;
893                 }
894
895                 switch (dent->d_type) {
896                 case DT_UNKNOWN:
897                         llapi_err(LLAPI_MSG_ERROR, 
898                                   "error: %s: '%s' is UNKNOWN type %d",
899                                   __FUNCTION__, dent->d_name, dent->d_type);
900                         break;
901                 case DT_DIR:
902                         ret = llapi_semantic_traverse(path, size, d, sem_init,
903                                                       sem_fini, data, dent);
904                         if (ret < 0)
905                                 goto out;
906                         break;
907                 default:
908                         ret = 0;
909                         if (sem_init) {
910                                 ret = sem_init(path, d, NULL, data, dent);
911                                 if (ret < 0)
912                                         goto out;
913                         }
914                         if (sem_fini && ret == 0)
915                                 sem_fini(path, d, NULL, data, dent);
916                 }
917         }
918
919 out:
920         path[len] = 0;
921
922         if (sem_fini)
923                 sem_fini(path, parent, d, data, de);
924 err:
925         if (d)
926                 closedir(d);
927         if (p)
928                 closedir(p);
929         return ret;
930 }
931
932 /* Check if the value matches 1 of the given criteria (e.g. --atime +/-N).
933  * @mds indicates if this is MDS timestamps and there are attributes on OSTs.
934  *
935  * The result is -1 if it does not match, 0 if not yet clear, 1 if matches.
936  * The table bolow gives the answers for the specified parameters (value and
937  * sign), 1st column is the answer for the MDS value, the 2nd is for the OST:
938  * --------------------------------------
939  * 1 | file > limit; sign > 0 | -1 / -1 |
940  * 2 | file = limit; sign > 0 |  ? /  1 |
941  * 3 | file < limit; sign > 0 |  ? /  1 |
942  * 4 | file > limit; sign = 0 | -1 / -1 |
943  * 5 | file = limit; sign = 0 |  ? /  1 |  <- (see the Note below)
944  * 6 | file < limit; sign = 0 |  ? / -1 |
945  * 7 | file > limit; sign < 0 |  1 /  1 |
946  * 8 | file = limit; sign < 0 |  ? / -1 |
947  * 9 | file < limit; sign < 0 |  ? / -1 |
948  * --------------------------------------
949  * Note: 5th actually means that the value is within the interval
950  * (limit - margin, limit]. */
951 static int find_value_cmp(unsigned int file, unsigned int limit, int sign,
952                           unsigned long long margin, int mds)
953 {
954         if (sign > 0) {
955                 if (file < limit)
956                         return mds ? 0 : 1;
957         }
958
959         if (sign == 0) {
960                 if (file <= limit && file + margin > limit)
961                         return mds ? 0 : 1;
962                 if (file + margin <= limit)
963                         return mds ? 0 : -1;
964         }
965
966         if (sign < 0) {
967                 if (file > limit)
968                         return 1;
969                 if (mds)
970                         return 0;
971         }
972
973         return -1;
974 }
975
976 /* Check if the file time matches all the given criteria (e.g. --atime +/-N).
977  * Return -1 or 1 if file timestamp does not or does match the given criteria
978  * correspondingly. Return 0 if the MDS time is being checked and there are
979  * attributes on OSTs and it is not yet clear if the timespamp matches.
980  *
981  * If 0 is returned, we need to do another RPC to the OSTs to obtain the
982  * updated timestamps. */
983 static int find_time_check(lstat_t *st, struct find_param *param, int mds)
984 {
985         int ret;
986         int rc = 0;
987
988         /* Check if file is accepted. */
989         if (param->atime) {
990                 ret = find_value_cmp(st->st_atime, param->atime,
991                                      param->asign, 24 * 60 * 60, mds);
992                 if (ret < 0)
993                         return ret;
994                 rc = ret;
995         }
996
997         if (param->mtime) {
998                 ret = find_value_cmp(st->st_mtime, param->mtime,
999                                      param->msign, 24 * 60 * 60, mds);
1000                 if (ret < 0)
1001                         return ret;
1002
1003                 /* If the previous check matches, but this one is not yet clear,
1004                  * we should return 0 to do an RPC on OSTs. */
1005                 if (rc == 1)
1006                         rc = ret;
1007         }
1008
1009         if (param->ctime) {
1010                 ret = find_value_cmp(st->st_ctime, param->ctime,
1011                                      param->csign, 24 * 60 * 60, mds);
1012                 if (ret < 0)
1013                         return ret;
1014
1015                 /* If the previous check matches, but this one is not yet clear,
1016                  * we should return 0 to do an RPC on OSTs. */
1017                 if (rc == 1)
1018                         rc = ret;
1019         }
1020
1021         return rc;
1022 }
1023
1024 static int cb_find_init(char *path, DIR *parent, DIR *dir,
1025                         void *data, cfs_dirent_t *de)
1026 {
1027         struct find_param *param = (struct find_param *)data;
1028         int decision = 1; /* 1 is accepted; -1 is rejected. */
1029         lstat_t *st = &param->lmd->lmd_st;
1030         int lustre_fs = 1;
1031         int checked_type = 0;
1032         int ret = 0;
1033
1034         LASSERT(parent != NULL || dir != NULL);
1035
1036         param->lmd->lmd_lmm.lmm_stripe_count = 0;
1037
1038         /* If a regular expression is presented, make the initial decision */
1039         if (param->pattern != NULL) {
1040                 char *fname = strrchr(path, '/');
1041                 fname = (fname == NULL ? path : fname + 1);
1042                 ret = fnmatch(param->pattern, fname, 0);
1043                 if ((ret == FNM_NOMATCH && !param->exclude_pattern) ||
1044                     (ret == 0 && param->exclude_pattern))
1045                         goto decided;
1046         }
1047
1048         /* See if we can check the file type from the dirent. */
1049         if (param->type && de != NULL && de->d_type != DT_UNKNOWN &&
1050             de->d_type < DT_MAX) {
1051                 checked_type = 1;
1052                 if (llapi_dir_filetype_table[de->d_type] == param->type) {
1053                         if (param->exclude_type)
1054                                 goto decided;
1055                 } else {
1056                         if (!param->exclude_type)
1057                                 goto decided;
1058                 }
1059         }
1060
1061
1062         /* If a time or OST should be checked, the decision is not taken yet. */
1063         if (param->atime || param->ctime || param->mtime || param->obduuid ||
1064             param->size)
1065                 decision = 0;
1066
1067         ret = 0;
1068         /* Request MDS for the stat info. */
1069         if (param->have_fileinfo == 0) {
1070                 if (dir) {
1071                         /* retrieve needed file info */
1072                         ret = ioctl(dirfd(dir), LL_IOC_MDC_GETINFO,
1073                                     (void *)param->lmd);
1074                 } else {
1075                         char *fname = strrchr(path, '/');
1076                         fname = (fname == NULL ? path : fname + 1);
1077
1078                         /* retrieve needed file info */
1079                         strncpy((char *)param->lmd, fname, param->lumlen);
1080                         ret = ioctl(dirfd(parent), IOC_MDC_GETFILEINFO,
1081                                    (void *)param->lmd);
1082                 }
1083         }
1084
1085         if (ret) {
1086                 if (errno == ENOTTY) {
1087                         /* ioctl is not supported, it is not a lustre fs.
1088                          * Do the regular lstat(2) instead. */
1089                         lustre_fs = 0;
1090                         ret = lstat_f(path, st);
1091                         if (ret) {
1092                                 llapi_err(LLAPI_MSG_ERROR, 
1093                                           "error: %s: lstat failed for %s",
1094                                           __FUNCTION__, path);
1095                                 return ret;
1096                         }
1097                 } else if (errno == ENOENT) {
1098                         llapi_err(LLAPI_MSG_WARN, 
1099                                   "warning: %s: %s does not exist",
1100                                   __FUNCTION__, path);
1101                         goto decided;
1102                 } else {
1103                         llapi_err(LLAPI_MSG_ERROR, "error: %s: %s failed for %s",
1104                                   __FUNCTION__, dir ? "LL_IOC_MDC_GETINFO" :
1105                                   "IOC_MDC_GETFILEINFO", path);
1106                         return ret;
1107                 }
1108         }
1109
1110         if (param->type && !checked_type) {
1111                 if ((st->st_mode & S_IFMT) == param->type) {
1112                         if (param->exclude_type)
1113                                 goto decided;
1114                 } else {
1115                         if (!param->exclude_type)
1116                                 goto decided;
1117                 }
1118         }
1119
1120         /* Prepare odb. */
1121         if (param->obduuid) {
1122                 if (lustre_fs && param->got_uuids &&
1123                     param->st_dev != st->st_dev) {
1124                         /* A lustre/lustre mount point is crossed. */
1125                         param->got_uuids = 0;
1126                         param->obds_printed = 0;
1127                         param->obdindex = OBD_NOT_FOUND;
1128                 }
1129
1130                 if (lustre_fs && !param->got_uuids) {
1131                         ret = setup_obd_indexes(dir ? dir : parent, param);
1132                         if (ret)
1133                                 return ret;
1134
1135                         param->st_dev = st->st_dev;
1136                 } else if (!lustre_fs && param->got_uuids) {
1137                         /* A lustre/non-lustre mount point is crossed. */
1138                         param->got_uuids = 0;
1139                         param->obdindex = OBD_NOT_FOUND;
1140                 }
1141         }
1142
1143         /* If an OBD UUID is specified but no one matches, skip this file. */
1144         if (param->obduuid && param->obdindex == OBD_NOT_FOUND)
1145                 goto decided;
1146
1147         /* If a OST UUID is given, and some OST matches, check it here. */
1148         if (param->obdindex != OBD_NOT_FOUND) {
1149                 if (!S_ISREG(st->st_mode))
1150                         goto decided;
1151
1152                 /* Only those files should be accepted, which have a
1153                  * stripe on the specified OST. */
1154                 if (!param->lmd->lmd_lmm.lmm_stripe_count) {
1155                         goto decided;
1156                 } else {
1157                         int i, j;
1158                         for (i = 0;
1159                              i < param->lmd->lmd_lmm.lmm_stripe_count; i++) {
1160                                 for (j = 0; j < param->num_obds; j++) {
1161                                         if (param->obdindexes[j] ==
1162                                             param->lmd->lmd_lmm.lmm_objects[i].l_ost_idx)
1163                                                 goto obd_matches;
1164                                 }
1165                         }
1166
1167                         if (i == param->lmd->lmd_lmm.lmm_stripe_count)
1168                                 goto decided;
1169                 }
1170         }
1171
1172         if (param->check_uid) {
1173                 if (st->st_uid == param->uid) {
1174                         if (param->exclude_uid)
1175                                 goto decided;
1176                 } else {
1177                         if (!param->exclude_uid)
1178                                 goto decided;
1179                 }
1180         }
1181
1182         if (param->check_gid) {
1183                 if (st->st_gid == param->gid) {
1184                         if (param->exclude_gid)
1185                                 goto decided;
1186                 } else {
1187                         if (!param->exclude_gid)
1188                                 goto decided;
1189                 }
1190         }
1191
1192         /* Check the time on mds. */
1193         if (!decision) {
1194                 int for_mds;
1195
1196                 for_mds = lustre_fs ? (S_ISREG(st->st_mode) &&
1197                                        param->lmd->lmd_lmm.lmm_stripe_count)
1198                                     : 0;
1199                 decision = find_time_check(st, param, for_mds);
1200         }
1201
1202 obd_matches:
1203         /* If file still fits the request, ask osd for updated info.
1204            The regulat stat is almost of the same speed as some new
1205            'glimpse-size-ioctl'. */
1206         if (!decision && S_ISREG(st->st_mode) &&
1207             (param->lmd->lmd_lmm.lmm_stripe_count || param->size)) {
1208                 if (param->obdindex != OBD_NOT_FOUND) {
1209                         /* Check whether the obd is active or not, if it is
1210                          * not active, just print the object affected by this
1211                          * failed ost 
1212                          * */
1213                         struct obd_statfs stat_buf;
1214                         struct obd_uuid uuid_buf;
1215
1216                         memset(&stat_buf, 0, sizeof(struct obd_statfs));
1217                         memset(&uuid_buf, 0, sizeof(struct obd_uuid));
1218                         ret = llapi_obd_statfs(path, LL_STATFS_LOV,
1219                                                param->obdindex, &stat_buf, 
1220                                                &uuid_buf);
1221                         if (ret) {
1222                                 if (ret == -ENODATA || ret == -ENODEV 
1223                                     || ret == -EIO)
1224                                         errno = EIO;
1225                                 llapi_printf(LLAPI_MSG_NORMAL, 
1226                                              "obd_uuid: %s failed %s ",
1227                                              param->obduuid->uuid, 
1228                                              strerror(errno));
1229                                 goto print_path;
1230                         }
1231                 }
1232                 if (dir) {
1233                         ret = ioctl(dirfd(dir), IOC_LOV_GETINFO,
1234                                     (void *)param->lmd);
1235                 } else if (parent) {
1236                         ret = ioctl(dirfd(parent), IOC_LOV_GETINFO,
1237                                     (void *)param->lmd);
1238                 }
1239
1240                 if (ret) {
1241                         if (errno == ENOENT) {
1242                                 llapi_err(LLAPI_MSG_ERROR, 
1243                                           "warning: %s: %s does not exist",
1244                                           __FUNCTION__, path);
1245                                 goto decided;
1246                         } else {
1247                                 llapi_err(LLAPI_MSG_ERROR, 
1248                                           "%s: IOC_LOV_GETINFO on %s failed",
1249                                           __FUNCTION__, path);
1250                                 return ret;
1251                         }
1252                 }
1253
1254                 /* Check the time on osc. */
1255                 decision = find_time_check(st, param, 0);
1256                 if (decision == -1)
1257                         goto decided;
1258         }
1259
1260         if (param->size)
1261                 decision = find_value_cmp(st->st_size, param->size,
1262                                           param->size_sign, param->size_units,
1263                                           0);
1264
1265 print_path:
1266         if (decision != -1) {
1267                 llapi_printf(LLAPI_MSG_NORMAL, "%s", path);
1268                 if (param->zeroend)
1269                         llapi_printf(LLAPI_MSG_NORMAL, "%c", '\0');
1270                 else
1271                         llapi_printf(LLAPI_MSG_NORMAL, "\n");
1272         }
1273
1274 decided:
1275         /* Do not get down anymore? */
1276         if (param->depth == param->maxdepth)
1277                 return 1;
1278
1279         param->depth++;
1280         return 0;
1281 }
1282
1283 static int cb_common_fini(char *path, DIR *parent, DIR *d, void *data,
1284                           cfs_dirent_t *de)
1285 {
1286         struct find_param *param = (struct find_param *)data;
1287         param->depth--;
1288         return 0;
1289 }
1290
1291 int llapi_find(char *path, struct find_param *param)
1292 {
1293         char *buf;
1294         int ret, len = strlen(path);
1295
1296         if (len > PATH_MAX) {
1297                 llapi_err(LLAPI_MSG_ERROR, "%s: Path name '%s' is too long",
1298                           __FUNCTION__, path);
1299                 return -EINVAL;
1300         }
1301
1302         buf = (char *)malloc(PATH_MAX + 1);
1303         if (!buf)
1304                 return -ENOMEM;
1305
1306         ret = common_param_init(param);
1307         if (ret) {
1308                 free(buf);
1309                 return ret;
1310         }
1311
1312         param->depth = 0;
1313
1314         strncpy(buf, path, PATH_MAX + 1);
1315         ret = llapi_semantic_traverse(buf, PATH_MAX + 1, NULL, cb_find_init,
1316                                       cb_common_fini, param, NULL);
1317
1318         find_param_fini(param);
1319         free(buf);
1320         return ret < 0 ? ret : 0;
1321 }
1322
1323 static int cb_getstripe(char *path, DIR *parent, DIR *d, void *data,
1324                         cfs_dirent_t *de)
1325 {
1326         struct find_param *param = (struct find_param *)data;
1327         int ret = 0;
1328
1329         LASSERT(parent != NULL || d != NULL);
1330
1331         /* Prepare odb. */
1332         if (!param->got_uuids) {
1333                 ret = setup_obd_uuid(d ? d : parent, path, param);
1334                 if (ret)
1335                         return ret;
1336         }
1337
1338         if (d) {
1339                 ret = ioctl(dirfd(d), LL_IOC_LOV_GETSTRIPE,
1340                             (void *)&param->lmd->lmd_lmm);
1341         } else if (parent) {
1342                 char *fname = strrchr(path, '/');
1343                 fname = (fname == NULL ? path : fname + 1);
1344
1345                 strncpy((char *)&param->lmd->lmd_lmm, fname, param->lumlen);
1346                 ret = ioctl(dirfd(parent), IOC_MDC_GETFILESTRIPE,
1347                             (void *)&param->lmd->lmd_lmm);
1348         }
1349
1350         if (ret) {
1351                 if (errno == ENODATA) {
1352                         if (!param->obduuid && !param->quiet)
1353                                 llapi_printf(LLAPI_MSG_NORMAL, 
1354                                              "%s has no stripe info\n", path);
1355                         goto out;
1356                 } else if (errno == ENOTTY) {
1357                         llapi_err(LLAPI_MSG_ERROR, 
1358                                   "%s: '%s' not on a Lustre fs?",
1359                                   __FUNCTION__, path);
1360                 } else if (errno == ENOENT) {
1361                         llapi_err(LLAPI_MSG_WARN, 
1362                                   "warning: %s: %s does not exist",
1363                                   __FUNCTION__, path);
1364                         goto out;
1365                 } else {
1366                         llapi_err(LLAPI_MSG_ERROR, 
1367                                   "error: %s: %s failed for %s",
1368                                    __FUNCTION__, d ? "LL_IOC_LOV_GETSTRIPE" :
1369                                   "IOC_MDC_GETFILESTRIPE", path);
1370                 }
1371
1372                 return ret;
1373         }
1374
1375         llapi_lov_dump_user_lmm(param, path, d ? 1 : 0);
1376 out:
1377         /* Do not get down anymore? */
1378         if (param->depth == param->maxdepth)
1379                 return 1;
1380
1381         param->depth++;
1382         return 0;
1383 }
1384
1385 int llapi_getstripe(char *path, struct find_param *param)
1386 {
1387         char *buf;
1388         int ret = 0, len = strlen(path);
1389
1390         if (len > PATH_MAX) {
1391                 llapi_err(LLAPI_MSG_ERROR, 
1392                           "%s: Path name '%s' is too long",
1393                           __FUNCTION__, path);
1394                 return -EINVAL;
1395         }
1396
1397         buf = (char *)malloc(PATH_MAX + 1);
1398         if (!buf)
1399                 return -ENOMEM;
1400
1401         ret = common_param_init(param);
1402         if (ret) {
1403                 free(buf);
1404                 return ret;
1405         }
1406
1407         param->depth = 0;
1408
1409         strncpy(buf, path, PATH_MAX + 1);
1410         ret = llapi_semantic_traverse(buf, PATH_MAX + 1, NULL, cb_getstripe,
1411                                       cb_common_fini, param, NULL);
1412         find_param_fini(param);
1413         free(buf);
1414         return ret < 0 ? ret : 0;
1415 }
1416
1417 int llapi_obd_statfs(char *path, __u32 type, __u32 index,
1418                      struct obd_statfs *stat_buf,
1419                      struct obd_uuid *uuid_buf)
1420 {
1421         int fd;
1422         char raw[OBD_MAX_IOCTL_BUFFER] = {'\0'};
1423         char *rawbuf = raw;
1424         struct obd_ioctl_data data = { 0 };
1425         int rc = 0;
1426
1427         data.ioc_inlbuf1 = (char *)&type;
1428         data.ioc_inllen1 = sizeof(__u32);
1429         data.ioc_inlbuf2 = (char *)&index;
1430         data.ioc_inllen2 = sizeof(__u32);
1431         data.ioc_pbuf1 = (char *)stat_buf;
1432         data.ioc_plen1 = sizeof(struct obd_statfs);
1433         data.ioc_pbuf2 = (char *)uuid_buf;
1434         data.ioc_plen2 = sizeof(struct obd_uuid);
1435
1436         if ((rc = obd_ioctl_pack(&data, &rawbuf, sizeof(raw))) != 0) {
1437                 llapi_err(LLAPI_MSG_ERROR, 
1438                           "llapi_obd_statfs: error packing ioctl data");
1439                 return rc;
1440         }
1441
1442         fd = open(path, O_RDONLY);
1443         if (errno == EISDIR)
1444                 fd = open(path, O_DIRECTORY | O_RDONLY);
1445
1446         if (fd < 0) {
1447                 rc = errno ? -errno : -EBADF;
1448                 llapi_err(LLAPI_MSG_ERROR, "error: %s: opening '%s'", 
1449                           __FUNCTION__, path);
1450                 return rc;
1451         }
1452         rc = ioctl(fd, IOC_OBD_STATFS, (void *)rawbuf);
1453         if (rc)
1454                 rc = errno ? -errno : -EINVAL;
1455
1456         close(fd);
1457         return rc;
1458 }
1459
1460 #define MAX_STRING_SIZE 128
1461 #define DEVICES_LIST "/proc/fs/lustre/devices"
1462
1463 int llapi_ping(char *obd_type, char *obd_name)
1464 {
1465         char path[MAX_STRING_SIZE];
1466         char buf[1];
1467         int rc, fd;
1468
1469         snprintf(path, MAX_STRING_SIZE, "/proc/fs/lustre/%s/%s/ping",
1470                  obd_type, obd_name);
1471
1472         fd = open(path, O_WRONLY);
1473         if (fd < 0) {
1474                 rc = errno;
1475                 llapi_err(LLAPI_MSG_ERROR, "error opening %s", path);
1476                 return rc;
1477         }
1478
1479         rc = write(fd, buf, 1);
1480         close(fd);
1481
1482         if (rc == 1)
1483                 return 0;
1484         return rc;
1485 }
1486
1487 int llapi_target_iterate(int type_num, char **obd_type, void *args, llapi_cb_t cb)
1488 {
1489         char buf[MAX_STRING_SIZE];
1490         FILE *fp = fopen(DEVICES_LIST, "r");
1491         int i, rc = 0;
1492
1493         if (fp == NULL) {
1494                 rc = errno;
1495                 llapi_err(LLAPI_MSG_ERROR, "error: opening "DEVICES_LIST);
1496                 return rc;
1497         }
1498
1499         while (fgets(buf, sizeof(buf), fp) != NULL) {
1500                 char *obd_type_name = NULL;
1501                 char *obd_name = NULL;
1502                 char *obd_uuid = NULL;
1503                 char rawbuf[OBD_MAX_IOCTL_BUFFER];
1504                 char *bufl = rawbuf;
1505                 char *bufp = buf;
1506                 struct obd_ioctl_data datal = { 0, };
1507                 struct obd_statfs osfs_buffer;
1508
1509                 while(bufp[0] == ' ')
1510                         ++bufp;
1511
1512                 for(i = 0; i < 3; i++) {
1513                         obd_type_name = strsep(&bufp, " ");
1514                 }
1515                 obd_name = strsep(&bufp, " ");
1516                 obd_uuid = strsep(&bufp, " ");
1517
1518                 memset(&osfs_buffer, 0, sizeof (osfs_buffer));
1519
1520                 memset(bufl, 0, sizeof(rawbuf));
1521                 datal.ioc_pbuf1 = (char *)&osfs_buffer;
1522                 datal.ioc_plen1 = sizeof(osfs_buffer);
1523
1524                 for (i = 0; i < type_num; i++) {
1525                         if (strcmp(obd_type_name, obd_type[i]) != 0)
1526                                 continue;
1527
1528                         cb(obd_type_name, obd_name, obd_uuid, args);
1529                 }
1530         }
1531         fclose(fp);
1532         return rc;
1533 }
1534
1535 static void do_target_check(char *obd_type_name, char *obd_name,
1536                             char *obd_uuid, void *args)
1537 {
1538         int rc;
1539
1540         rc = llapi_ping(obd_type_name, obd_name);
1541         if (rc == ENOTCONN) {
1542                 llapi_printf(LLAPI_MSG_NORMAL, "%s inactive.\n", obd_name);
1543         } else if (rc) {
1544                 llapi_err(LLAPI_MSG_ERROR, "error: check '%s'", obd_name);
1545         } else {
1546                 llapi_printf(LLAPI_MSG_NORMAL, "%s active.\n", obd_name);
1547         }
1548 }
1549
1550 int llapi_target_check(int type_num, char **obd_type, char *dir)
1551 {
1552         return llapi_target_iterate(type_num, obd_type, NULL, do_target_check);
1553 }
1554
1555 #undef MAX_STRING_SIZE
1556
1557 int llapi_catinfo(char *dir, char *keyword, char *node_name)
1558 {
1559         char raw[OBD_MAX_IOCTL_BUFFER];
1560         char out[LLOG_CHUNK_SIZE];
1561         char *buf = raw;
1562         struct obd_ioctl_data data = { 0 };
1563         char key[30];
1564         DIR *root;
1565         int rc;
1566
1567         sprintf(key, "%s", keyword);
1568         memset(raw, 0, sizeof(raw));
1569         memset(out, 0, sizeof(out));
1570         data.ioc_inlbuf1 = key;
1571         data.ioc_inllen1 = strlen(key) + 1;
1572         if (node_name) {
1573                 data.ioc_inlbuf2 = node_name;
1574                 data.ioc_inllen2 = strlen(node_name) + 1;
1575         }
1576         data.ioc_pbuf1 = out;
1577         data.ioc_plen1 = sizeof(out);
1578         rc = obd_ioctl_pack(&data, &buf, sizeof(raw));
1579         if (rc)
1580                 return rc;
1581
1582         root = opendir(dir);
1583         if (root == NULL) {
1584                 rc = errno;
1585                 llapi_err(LLAPI_MSG_ERROR, "open %s failed", dir);
1586                 return rc;
1587         }
1588
1589         rc = ioctl(dirfd(root), OBD_IOC_LLOG_CATINFO, buf);
1590         if (rc)
1591                 llapi_err(LLAPI_MSG_ERROR, "ioctl OBD_IOC_CATINFO failed");
1592         else
1593                 llapi_printf(LLAPI_MSG_NORMAL, "%s", data.ioc_pbuf1);
1594
1595         closedir(root);
1596         return rc;
1597 }
1598
1599 /* Is this a lustre fs? */
1600 int llapi_is_lustre_mnttype(const char *type)
1601 {
1602         return (strcmp(type, "lustre") == 0 || strcmp(type,"lustre_lite") == 0);
1603 }
1604
1605 /* Is this a lustre client fs? */
1606 int llapi_is_lustre_mnt(struct mntent *mnt)
1607 {
1608         return (llapi_is_lustre_mnttype(mnt->mnt_type) &&
1609                 strstr(mnt->mnt_fsname, ":/") != NULL);
1610 }
1611
1612 int llapi_quotacheck(char *mnt, int check_type)
1613 {
1614         DIR *root;
1615         int rc;
1616
1617         root = opendir(mnt);
1618         if (!root) {
1619                 llapi_err(LLAPI_MSG_ERROR, "open %s failed", mnt);
1620                 return -1;
1621         }
1622
1623         rc = ioctl(dirfd(root), LL_IOC_QUOTACHECK, check_type);
1624
1625         closedir(root);
1626         return rc;
1627 }
1628
1629 int llapi_poll_quotacheck(char *mnt, struct if_quotacheck *qchk)
1630 {
1631         DIR *root;
1632         int poll_intvl = 2;
1633         int rc;
1634
1635         root = opendir(mnt);
1636         if (!root) {
1637                 llapi_err(LLAPI_MSG_ERROR, "open %s failed", mnt);
1638                 return -1;
1639         }
1640
1641         while (1) {
1642                 rc = ioctl(dirfd(root), LL_IOC_POLL_QUOTACHECK, qchk);
1643                 if (!rc)
1644                         break;
1645                 sleep(poll_intvl);
1646                 if (poll_intvl < 30)
1647                         poll_intvl *= 2;
1648         }
1649
1650         closedir(root);
1651         return rc;
1652 }
1653
1654 int llapi_quotactl(char *mnt, struct if_quotactl *qctl)
1655 {
1656         DIR *root;
1657         int rc;
1658
1659         root = opendir(mnt);
1660         if (!root) {
1661                 llapi_err(LLAPI_MSG_ERROR, "open %s failed", mnt);
1662                 return -1;
1663         }
1664
1665         rc = ioctl(dirfd(root), LL_IOC_QUOTACTL, qctl);
1666
1667         closedir(root);
1668         return rc;
1669 }
1670
1671 static int cb_quotachown(char *path, DIR *parent, DIR *d, void *data,
1672                          cfs_dirent_t *de)
1673 {
1674         struct find_param *param = (struct find_param *)data;
1675         lstat_t *st;
1676         int rc;
1677
1678         LASSERT(parent != NULL || d != NULL);
1679
1680         if (d) {
1681                 rc = ioctl(dirfd(d), LL_IOC_MDC_GETINFO,
1682                            (void *)param->lmd);
1683         } else if (parent) {
1684                 char *fname = strrchr(path, '/');
1685                 fname = (fname == NULL ? path : fname + 1);
1686
1687                 strncpy((char *)param->lmd, fname, param->lumlen);
1688                 rc = ioctl(dirfd(parent), IOC_MDC_GETFILEINFO,
1689                            (void *)param->lmd);
1690         } else {
1691                 return 0;
1692         }
1693
1694         if (rc) {
1695                 if (errno == ENODATA) {
1696                         if (!param->obduuid && !param->quiet)
1697                                 llapi_err(LLAPI_MSG_ERROR, 
1698                                           "%s has no stripe info", path);
1699                         rc = 0;
1700                 } else if (errno == ENOENT) {
1701                         llapi_err(LLAPI_MSG_ERROR, 
1702                                   "warning: %s: %s does not exist",
1703                                   __FUNCTION__, path);
1704                         rc = 0;
1705                 } else if (errno != EISDIR) {
1706                         rc = errno;
1707                         llapi_err(LLAPI_MSG_ERROR, "%s ioctl failed for %s.",
1708                                   d ? "LL_IOC_MDC_GETINFO" :
1709                                   "IOC_MDC_GETFILEINFO", path);
1710                 }
1711                 return rc;
1712         }
1713
1714         st = &param->lmd->lmd_st;
1715
1716         /* libc chown() will do extra check, and if the real owner is
1717          * the same as the ones to set, it won't fall into kernel, so
1718          * invoke syscall directly. */
1719         rc = syscall(SYS_chown, path, -1, -1);
1720         if (rc)
1721                 llapi_err(LLAPI_MSG_ERROR,"error: chown %s (%u,%u)", path);
1722
1723         rc = chmod(path, st->st_mode);
1724         if (rc)
1725                 llapi_err(LLAPI_MSG_ERROR,"error: chmod %s (%hu)", path, st->st_mode);
1726
1727         return rc;
1728 }
1729
1730 int llapi_quotachown(char *path, int flag)
1731 {
1732         struct find_param param;
1733         char *buf;
1734         int ret = 0, len = strlen(path);
1735
1736         if (len > PATH_MAX) {
1737                 llapi_err(LLAPI_MSG_ERROR, "%s: Path name '%s' is too long",
1738                           __FUNCTION__, path);
1739                 return -EINVAL;
1740         }
1741
1742         buf = (char *)malloc(PATH_MAX + 1);
1743         if (!buf)
1744                 return -ENOMEM;
1745
1746         memset(&param, 0, sizeof(param));
1747         param.recursive = 1;
1748         param.verbose = 0;
1749         param.quiet = 1;
1750
1751         ret = common_param_init(&param);
1752         if (ret)
1753                 goto out;
1754
1755         strncpy(buf, path, PATH_MAX + 1);
1756         ret = llapi_semantic_traverse(buf, PATH_MAX + 1, NULL, cb_quotachown,
1757                                       NULL, &param, NULL);
1758 out:
1759         find_param_fini(&param);
1760         free(buf);
1761         return ret;
1762 }
1763
1764 #include <pwd.h>
1765 #include <grp.h>
1766 #include <mntent.h>
1767 #include <sys/wait.h>
1768 #include <errno.h>
1769 #include <ctype.h>
1770
1771 static int rmtacl_notify(int ops)
1772 {
1773         FILE *fp;
1774         struct mntent *mnt;
1775         int found = 0, fd, rc;
1776
1777         fp = setmntent(MOUNTED, "r");
1778         if (fp == NULL) {
1779                 perror("setmntent");
1780                 return -1;
1781         }
1782
1783         while (1) {
1784                 mnt = getmntent(fp);
1785                 if (!mnt)
1786                         break;
1787
1788                 if (!llapi_is_lustre_mnt(mnt))
1789                         continue;
1790
1791                 fd = open(mnt->mnt_dir, O_RDONLY | O_DIRECTORY);
1792                 if (fd < 0) {
1793                         perror("open");
1794                         return -1;
1795                 }
1796
1797                 rc = ioctl(fd, LL_IOC_RMTACL, ops);
1798                 if (rc < 0) {
1799                         perror("ioctl");
1800                 return -1;
1801         }
1802
1803                 found++;
1804         }
1805         endmntent(fp);
1806         return found;
1807 }
1808
1809 static char *next_token(char *p, int div)
1810 {
1811         if (p == NULL)
1812                 return NULL;
1813
1814         if (div)
1815                 while (*p && *p != ':' && !isspace(*p))
1816                         p++;
1817         else
1818                 while (*p == ':' || isspace(*p))
1819                         p++;
1820
1821         return *p ? p : NULL;
1822 }
1823
1824 static int rmtacl_name2id(char *name, int is_user)
1825 {
1826         if (is_user) {
1827                 struct passwd *pw;
1828
1829                 if ((pw = getpwnam(name)) == NULL)
1830                         return INVALID_ID;
1831                 else
1832                         return (int)(pw->pw_uid);
1833         } else {
1834                 struct group *gr;
1835
1836                 if ((gr = getgrnam(name)) == NULL)
1837                         return INVALID_ID;
1838                 else
1839                         return (int)(gr->gr_gid);
1840         }
1841 }
1842
1843 static int isodigit(int c)
1844 {
1845         return (c >= '0' && c <= '7') ? 1 : 0;
1846 }
1847
1848 /*
1849  * Whether the name is just digits string (uid/gid) already or not.
1850  * Return value:
1851  * 1: str is id
1852  * 0: str is not id
1853  */
1854 static int str_is_id(char *str)
1855 {
1856         if (str == NULL)
1857                 return 0;
1858
1859         if (*str == '0') {
1860                 str++;
1861                 if (*str == 'x' || *str == 'X') { /* for Hex. */
1862                         if (!isxdigit(*(++str)))
1863                                 return 0;
1864
1865                         while (isxdigit(*(++str)));
1866                 } else if (isodigit(*str)) { /* for Oct. */
1867                         while (isodigit(*(++str)));
1868                 }
1869         } else if (isdigit(*str)) { /* for Dec. */
1870                 while (isdigit(*(++str)));
1871         }
1872
1873         return (*str == 0) ? 1 : 0;
1874 }
1875
1876 typedef struct {
1877         char *name;
1878         int   length;
1879         int   is_user;
1880         int   next_token;
1881 } rmtacl_name_t;
1882
1883 #define RMTACL_OPTNAME(name) name, sizeof(name) - 1
1884
1885 static rmtacl_name_t rmtacl_namelist[] = {
1886         { RMTACL_OPTNAME("user:"),            1,      0 },
1887         { RMTACL_OPTNAME("group:"),           0,      0 },
1888         { RMTACL_OPTNAME("default:user:"),    1,      0 },
1889         { RMTACL_OPTNAME("default:group:"),   0,      0 },
1890         /* for --tabular option */
1891         { RMTACL_OPTNAME("user"),             1,      1 },
1892         { RMTACL_OPTNAME("group"),            0,      1 },
1893         { 0 }
1894 };
1895
1896 static int rgetfacl_output(char *str)
1897 {
1898         char *start = NULL, *end = NULL;
1899         int is_user = 0, n, id;
1900         char c;
1901         rmtacl_name_t *rn;
1902
1903         if (str == NULL)
1904                 return -1;
1905
1906         for (rn = rmtacl_namelist; rn->name; rn++) {
1907                 if(strncmp(str, rn->name, rn->length) == 0) {
1908                         if (!rn->next_token)
1909                                 start = str + rn->length;
1910                         else
1911                                 start = next_token(str + rn->length, 0);
1912                         is_user = rn->is_user;
1913                         break;
1914                 }
1915         }
1916
1917         end = next_token(start, 1);
1918         if (end == NULL || start == end) {
1919                 n = printf("%s", str);
1920                 return n;
1921         }
1922
1923         c = *end;
1924         *end = 0;
1925         id = rmtacl_name2id(start, is_user);
1926         if (id == INVALID_ID) {
1927                 if (str_is_id(start)) {
1928                         *end = c;
1929                         n = printf("%s", str);
1930                 } else
1931                         return -1;
1932         } else if ((id == NOBODY_UID && is_user) ||
1933                    (id == NOBODY_GID && !is_user)) {
1934                 *end = c;
1935                 n = printf("%s", str);
1936         } else {
1937                 *end = c;
1938                 *start = 0;
1939                 n = printf("%s%d%s", str, id, end);
1940         }
1941         return n;
1942 }
1943
1944 static int child_status(int status)
1945 {
1946         return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
1947 }
1948
1949 static int do_rmtacl(int argc, char *argv[], int ops, int (output_func)(char *))
1950 {
1951         pid_t pid = 0;
1952         int fd[2], status;
1953         FILE *fp;
1954         char buf[PIPE_BUF];
1955
1956         if (output_func) {
1957                 if (pipe(fd) < 0) {
1958                         perror("pipe");
1959                         return -1;
1960                 }
1961
1962                 if ((pid = fork()) < 0) {
1963                         perror("fork");
1964                         close(fd[0]);
1965                         close(fd[1]);
1966                         return -1;
1967                 } else if (!pid) {
1968                         /* child process redirects its output. */
1969                         close(fd[0]);
1970                         close(1);
1971                         if (dup2(fd[1], 1) < 0) {
1972                                 perror("dup2");
1973                                 close(fd[1]);
1974                                 return -1;
1975                         }
1976                 } else {
1977                         close(fd[1]);
1978                 }
1979         }
1980
1981         if (!pid) {
1982                 status = rmtacl_notify(ops);
1983                 if (status < 0)
1984                         return -1;
1985
1986                 exit(execvp(argv[0], argv));
1987         }
1988
1989         /* the following is parent process */
1990         if ((fp = fdopen(fd[0], "r")) == NULL) {
1991                 perror("fdopen");
1992                 kill(pid, SIGKILL);
1993                 close(fd[0]);
1994                 return -1;
1995         }
1996
1997         while (fgets(buf, PIPE_BUF, fp) != NULL) {
1998                 if (output_func(buf) < 0)
1999                         fprintf(stderr, "WARNING: unexpected error!\n[%s]\n",
2000                                 buf);
2001         }
2002         fclose(fp);
2003         close(fd[0]);
2004
2005         if (waitpid(pid, &status, 0) < 0) {
2006                 perror("waitpid");
2007                 return -1;
2008         }
2009
2010         return child_status(status);
2011 }
2012
2013 int llapi_lsetfacl(int argc, char *argv[])
2014 {
2015         return do_rmtacl(argc, argv, RMT_LSETFACL, NULL);
2016 }
2017
2018 int llapi_lgetfacl(int argc, char *argv[])
2019 {
2020         return do_rmtacl(argc, argv, RMT_LGETFACL, NULL);
2021 }
2022
2023 int llapi_rsetfacl(int argc, char *argv[])
2024 {
2025         return do_rmtacl(argc, argv, RMT_RSETFACL, NULL);
2026 }
2027
2028 int llapi_rgetfacl(int argc, char *argv[])
2029 {
2030         return do_rmtacl(argc, argv, RMT_RGETFACL, rgetfacl_output);
2031 }
2032
2033 int llapi_cp(int argc, char *argv[])
2034 {
2035         int rc;
2036
2037         rc = rmtacl_notify(RMT_RSETFACL);
2038         if (rc < 0)
2039                 return -1;
2040
2041         exit(execvp(argv[0], argv));
2042 }
2043
2044 int llapi_ls(int argc, char *argv[])
2045 {
2046         int rc;
2047
2048         rc = rmtacl_notify(RMT_LGETFACL);
2049         if (rc < 0)
2050                 return -1;
2051
2052         exit(execvp(argv[0], argv));
2053 }