Whamcloud - gitweb
LU-12516 mdd: support for volatile creation in .lustre
[fs/lustre-release.git] / lustre / tests / multiop.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2012, 2017, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  */
32
33 #ifndef _GNU_SOURCE
34 #define _GNU_SOURCE /* pull in O_DIRECTORY in bits/fcntl.h */
35 #endif
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <malloc.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/mman.h>
45 #include <sys/vfs.h>
46 #include <sys/ioctl.h>
47 #include <sys/xattr.h>
48 #include <sys/file.h>
49 #include <signal.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <semaphore.h>
53 #include <time.h>
54 #include <err.h>
55
56 #include <lustre/lustreapi.h>
57
58 #define T1 "write data before unlink\n"
59 #define T2 "write data after unlink\n"
60 char msg[] = "yabba dabba doo, I'm coming for you, I live in a shoe, I don't know what to do.\n'Bigger, bigger,and bigger yet!' cried the Creator.  'You are not yet substantial enough for my boundless intents!'  And ever greater and greater the object became, until all was lost 'neath its momentus bulk.\n";
61 char *buf, *buf_align;
62 int bufsize = 0;
63 sem_t sem;
64 #define ALIGN_LEN 65535
65 #define XATTR "user.multiop"
66
67 char usage[] =
68 "Usage: %s filename command-sequence [path...]\n"
69 "    command-sequence items:\n"
70 "        A  fsetxattr(\"user.multiop\")\n"
71 "        a[num] fgetxattr(\"user.multiop\") [optional buffer size, default 0]\n"
72 "        c  close\n"
73 "        B[num] call setstripe ioctl to create stripes\n"
74 "        C[num] create with optional stripes\n"
75 "        d  mkdir\n"
76 "        D  open(O_DIRECTORY)\n"
77 "        e[R|W|U] apply lease. R: Read; W: Write; U: Unlock\n"
78 "        E[+|-] get lease. +/-: expect lease to (not) exist\n"
79 "        f  statfs\n"
80 "        F  print FID\n"
81 "        H[num] create HSM released file with num stripes\n"
82 "        G gid get grouplock\n"
83 "        g gid put grouplock\n"
84 "        K  link path to filename\n"
85 "        L  link\n"
86 "        l  symlink filename to path\n"
87 "        m  mknod\n"
88 "        M  rw mmap to EOF (must open and stat prior)\n"
89 "        n  rename path to filename\n"
90 "        N  rename filename to path\n"
91 "        o  open(O_RDONLY)\n"
92 "        O  open(O_CREAT|O_RDWR)\n"
93 "        p  print return value of last command\n"
94 "        r[num] read [optional length]\n"
95 "        R  reference entire mmap-ed region\n"
96 "        s  stat\n"
97 "        S  fstat\n"
98 "        t  fchmod\n"
99 "        T[num] ftruncate [optional position, default 0]\n"
100 "        u  unlink\n"
101 "        U  munmap\n"
102 "        v  verbose\n"
103 "        V  open a volatile file\n"
104 "        w[num] write optional length\n"
105 "        x  get file data version\n"
106 "        W  write entire mmap-ed region\n"
107 "        y  fsync\n"
108 "        Y  fdatasync\n"
109 "        z[num] lseek(SEEK_SET) [optional offset, default 0]\n"
110 "        Z[num] lseek(SEEK_CUR) [optional offset, default 0]\n"
111 "        _  wait for signal\n";
112
113 void usr1_handler(int unused)
114 {
115         int saved_errno = errno;
116
117         /*
118          * signal(7): POSIX.1-2004 ...requires an implementation to guarantee
119          * that the following functions can be safely called inside a signal
120          * handler:
121          *            sem_post()
122          */
123         sem_post(&sem);
124
125         errno = saved_errno;
126 }
127
128 static const char *
129 pop_arg(int argc, char *argv[])
130 {
131         static int cur_arg = 3;
132
133         if (cur_arg >= argc)
134                 return NULL;
135
136         return argv[cur_arg++];
137 }
138
139 struct flag_mapping {
140         const char *string;
141         const int  flag;
142 } flag_table[] = {
143         {"O_RDONLY", O_RDONLY},
144         {"O_WRONLY", O_WRONLY},
145         {"O_RDWR", O_RDWR},
146         {"O_CREAT", O_CREAT},
147         {"O_EXCL", O_EXCL},
148         {"O_NOCTTY", O_NOCTTY},
149         {"O_TRUNC", O_TRUNC},
150         {"O_APPEND", O_APPEND},
151         {"O_NONBLOCK", O_NONBLOCK},
152         {"O_NDELAY", O_NDELAY},
153         {"O_SYNC", O_SYNC},
154 #ifdef O_DIRECT
155         {"O_DIRECT", O_DIRECT},
156 #endif
157 #ifdef O_NOATIME
158         {"O_NOATIME", O_NOATIME},
159 #endif
160         {"O_LARGEFILE", O_LARGEFILE},
161         {"O_DIRECTORY", O_DIRECTORY},
162         {"O_NOFOLLOW", O_NOFOLLOW},
163         {"O_LOV_DELAY_CREATE", O_LOV_DELAY_CREATE},
164         {"", -1}
165 };
166
167 int get_flags(char *data, int *rflags)
168 {
169         char *cloned_flags;
170         char *tmp;
171         int flag_set = 0;
172         int flags = 0;
173         int size = 0;
174
175         cloned_flags = strdup(data);
176         if (cloned_flags == NULL) {
177                 fprintf(stderr, "Insufficient memory.\n");
178                 exit(-1);
179         }
180
181         for (tmp = strtok(cloned_flags, ":"); tmp;
182              tmp = strtok(NULL, ":")) {
183                 int i;
184
185                 size = tmp - cloned_flags;
186                 for (i = 0; flag_table[i].flag != -1; i++) {
187                         if (!strcmp(tmp, flag_table[i].string)){
188                                 flags |= flag_table[i].flag;
189                                 size += strlen(flag_table[i].string);
190                                 flag_set = 1;
191                                 break;
192                         }
193                 }
194         }
195         free(cloned_flags);
196
197         if (!flag_set) {
198                 *rflags = O_RDONLY;
199                 return 0;
200         }
201
202         *rflags = flags;
203         return size;
204 }
205
206 #define POP_ARG() (pop_arg(argc, argv))
207
208 int main(int argc, char **argv)
209 {
210         char                    *fname, *commands;
211         const char              *newfile;
212         const char              *oldpath;
213         struct stat              st;
214         struct statfs            stfs;
215         size_t                   mmap_len = 0, i;
216         unsigned char           *mmap_ptr = NULL, junk = 1;
217         int                      len, fd = -1;
218         int                      flags;
219         int                      save_errno;
220         int                      verbose = 0;
221         int                      gid = 0;
222         struct lu_fid            fid;
223         struct timespec          ts;
224         struct lov_user_md_v3    lum;
225         char *xattr_buf = NULL;
226         size_t xattr_buf_size = 0;
227         long long rc = 0;
228         long long last_rc;
229
230         if (argc < 3) {
231                 fprintf(stderr, usage, argv[0]);
232                 exit(1);
233         }
234
235         memset(&st, 0, sizeof(st));
236         sem_init(&sem, 0, 0);
237         /* use sigaction instead of signal to avoid SA_ONESHOT semantics */
238         sigaction(SIGUSR1, &(const struct sigaction){.sa_handler = &usr1_handler},
239                   NULL);
240
241         fname = argv[1];
242
243         for (commands = argv[2]; *commands; commands++) {
244                 /* XXX Most commands return 0 or we exit so we only
245                  * update rc where really needed. */
246                 last_rc = rc;
247                 rc = 0;
248
249                 switch (*commands) {
250                 case '_':
251                         if (verbose) {
252                                 printf("PAUSING\n");
253                                 fflush(stdout);
254                         }
255                         len = atoi(commands+1);
256                         if (len <= 0)
257                                 len = 3600; /* 1 hour */
258                         ts.tv_sec = time(NULL) + len;
259                         ts.tv_nsec = 0;
260                         while (sem_timedwait(&sem, &ts) < 0 && errno == EINTR);
261                         break;
262                 case 'A':
263                         if (fsetxattr(fd, XATTR, "multiop", 8, 0)) {
264                                 save_errno = errno;
265                                 perror("fsetxattr");
266                                 exit(save_errno);
267                         }
268                         break;
269                 case 'a':
270                         len = atoi(commands + 1);
271                         if (xattr_buf_size < len) {
272                                 xattr_buf = realloc(xattr_buf, len);
273                                 if (xattr_buf == NULL) {
274                                         save_errno = errno;
275                                         perror("allocating xattr buffer\n");
276                                         exit(save_errno);
277                                 }
278
279                                 xattr_buf_size = len;
280                         }
281
282                         rc = fgetxattr(fd, XATTR, xattr_buf, len);
283                         if (rc < 0) {
284                                 save_errno = errno;
285                                 perror("fgetxattr");
286                                 exit(save_errno);
287                         }
288                         break;
289                 case 'c':
290                         if (close(fd) == -1) {
291                                 save_errno = errno;
292                                 perror("close");
293                                 exit(save_errno);
294                         }
295                         fd = -1;
296                         break;
297                 case 'B':
298                         lum = (struct lov_user_md_v3) {
299                                 .lmm_magic = LOV_USER_MAGIC_V3,
300                                 .lmm_stripe_count = atoi(commands + 1),
301                         };
302
303                         if (ioctl(fd, LL_IOC_LOV_SETSTRIPE, &lum) < 0) {
304                                 save_errno = errno;
305                                 perror("LL_IOC_LOV_SETSTRIPE");
306                                 exit(save_errno);
307                         }
308                         break;
309                 case 'C':
310                         len = atoi(commands+1);
311                         fd = llapi_file_open(fname, O_CREAT | O_WRONLY, 0644,
312                                              0, 0, len, 0);
313                         if (fd == -1) {
314                                 save_errno = errno;
315                                 perror("create stripe file");
316                                 exit(save_errno);
317                         }
318                         rc = fd;
319                         break;
320                 case 'd':
321                         if (mkdir(fname, 0755) == -1) {
322                                 save_errno = errno;
323                                 perror("mkdir(0755)");
324                                 exit(save_errno);
325                         }
326                         break;
327                 case 'D':
328                         fd = open(fname, O_DIRECTORY);
329                         if (fd == -1) {
330                                 save_errno = errno;
331                                 perror("open(O_DIRECTORY)");
332                                 exit(save_errno);
333                         }
334                         rc = fd;
335                         break;
336                 case 'e':
337                         commands++;
338                         switch (*commands) {
339                         case 'U':
340                                 rc = llapi_lease_release(fd);
341                                 break;
342                         case 'R':
343                                 rc = llapi_lease_acquire(fd, LL_LEASE_RDLCK);
344                                 break;
345                         case 'W':
346                                 rc = llapi_lease_acquire(fd, LL_LEASE_WRLCK);
347                                 break;
348                         default:
349                                 errx(-1, "unknown mode: %c", *commands);
350                         }
351                         if (rc < 0)
352                                 err(errno, "apply/unlock lease error");
353
354                         if (flags != LL_LEASE_UNLCK)
355                                 break;
356
357                         /* F_UNLCK, interpret return code */
358                         if (rc > 0) {
359                                 const char *str = "unknown";
360                                 if (rc == LL_LEASE_RDLCK)
361                                         str = "read";
362                                 else if (rc == LL_LEASE_WRLCK)
363                                         str = "write";
364                                 fprintf(stdout, "%s lease(%lld) released.\n",
365                                         str, rc);
366                         } else if (rc == 0) {
367                                 fprintf(stdout, "lease already broken.\n");
368                         }
369                         break;
370                 case 'E':
371                         commands++;
372                         if (*commands != '-' && *commands != '+')
373                                 errx(-1, "unknown mode: %c\n", *commands);
374
375                         rc = llapi_lease_check(fd);
376                         if (rc > 0) {
377                                 const char *str = "unknown";
378
379                                 if (rc == LL_LEASE_RDLCK)
380                                         str = "read";
381                                 else if (rc == LL_LEASE_WRLCK)
382                                         str = "write";
383                                 fprintf(stdout, "%s lease(%lld) has applied.\n",
384                                         str, rc);
385                                 if (*commands == '-')
386                                         errx(-1, "expect lease to not exist");
387                         } else if (rc == 0) {
388                                 fprintf(stdout, "no lease applied.\n");
389                                 if (*commands == '+')
390                                         errx(-1, "expect lease exists");
391                         } else {
392                                 err(errno, "free lease error");
393                         }
394                         break;
395                 case 'f':
396                         if (statfs(fname, &stfs) == -1)
397                                 errx(-1, "statfs()");
398                         break;
399                 case 'F':
400                         if (fd == -1)
401                                 rc = llapi_path2fid(fname, &fid);
402                         else
403                                 rc = llapi_fd2fid(fd, &fid);
404                         if (rc != 0)
405                                 fprintf(stderr,
406                                         "llapi_path/fd2fid() on %d, rc=%lld\n",
407                                         fd, rc);
408                         else
409                                 printf(DFID"\n", PFID(&fid));
410                         fflush(stdout);
411                         break;
412                 case 'G':
413                         gid = atoi(commands+1);
414                         if (ioctl(fd, LL_IOC_GROUP_LOCK, gid) == -1) {
415                                 save_errno = errno;
416                                 perror("ioctl(GROUP_LOCK)");
417                                 exit(save_errno);
418                         }
419                         break;
420                 case 'g':
421                         gid = atoi(commands+1);
422                         if (ioctl(fd, LL_IOC_GROUP_UNLOCK, gid) == -1) {
423                                 save_errno = errno;
424                                 perror("ioctl(GROUP_UNLOCK)");
425                                 exit(save_errno);
426                         }
427                         break;
428                 case 'H':
429                         len = atoi(commands+1);
430                         fd = llapi_file_open(fname, O_CREAT | O_WRONLY,
431                                 0644, 0, 0, len,
432                                 LOV_PATTERN_RAID0 | LOV_PATTERN_F_RELEASED);
433                         if (fd == -1) {
434                                 save_errno = errno;
435                                 perror("create stripe file");
436                                 exit(save_errno);
437                         }
438                         rc = fd;
439                         break;
440                 case 'j':
441                         if (flock(fd, LOCK_EX) == -1)
442                                 errx(-1, "flock()");
443                         break;
444                 case 'K':
445                         oldpath = POP_ARG();
446                         if (oldpath == NULL)
447                                 oldpath = fname;
448
449                         if (link(oldpath, fname)) {
450                                 save_errno = errno;
451                                 perror("link()");
452                                 exit(save_errno);
453                         }
454                         break;
455                 case 'l':
456                         newfile = POP_ARG();
457                         if (!newfile)
458                                 newfile = fname;
459                         if (symlink(fname, newfile)) {
460                                 save_errno = errno;
461                                 perror("symlink()");
462                                 exit(save_errno);
463                         }
464                         break;
465                 case 'L':
466                         newfile = POP_ARG();
467                         if (newfile == NULL)
468                                 newfile = fname;
469
470                         if (link(fname, newfile)) {
471                                 save_errno = errno;
472                                 perror("link()");
473                                 exit(save_errno);
474                         }
475                         break;
476                 case 'm':
477                         if (mknod(fname, S_IFREG | 0644, 0) == -1) {
478                                 save_errno = errno;
479                                 perror("mknod(S_IFREG|0644, 0)");
480                                 exit(save_errno);
481                         }
482                         break;
483                 case 'M':
484                         if (st.st_size == 0) {
485                                 fprintf(stderr, "mmap without preceeding stat, or on"
486                                         " zero length file.\n");
487                                 exit(-1);
488                         }
489                         mmap_len = st.st_size;
490                         mmap_ptr = mmap(NULL, mmap_len, PROT_WRITE | PROT_READ,
491                                         MAP_SHARED, fd, 0);
492                         if (mmap_ptr == MAP_FAILED) {
493                                 save_errno = errno;
494                                 perror("mmap");
495                                 exit(save_errno);
496                         }
497                         break;
498                 case 'n':
499                         oldpath = POP_ARG();
500                         if (oldpath == NULL)
501                                 oldpath = fname;
502
503                         if (rename(oldpath, fname) < 0) {
504                                 save_errno = errno;
505                                 perror("rename()");
506                                 exit(save_errno);
507                         }
508                         break;
509                 case 'N':
510                         newfile = POP_ARG();
511                         if (!newfile)
512                                 newfile = fname;
513                         if (rename (fname, newfile)) {
514                                 save_errno = errno;
515                                 perror("rename()");
516                                 exit(save_errno);
517                         }
518                         break;
519                 case 'O':
520                         fd = open(fname, O_CREAT|O_RDWR, 0644);
521                         if (fd == -1) {
522                                 save_errno = errno;
523                                 perror("open(O_RDWR|O_CREAT)");
524                                 exit(save_errno);
525                         }
526                         rc = fd;
527                         break;
528                 case 'o':
529                         len = get_flags(commands+1, &flags);
530                         commands += len;
531                         if (flags & O_CREAT)
532                                 fd = open(fname, flags, 0666);
533                         else
534                                 fd = open(fname, flags);
535                         if (fd == -1) {
536                                 save_errno = errno;
537                                 perror("open");
538                                 exit(save_errno);
539                         }
540                         rc = fd;
541                         break;
542                 case 'p':
543                         printf("%lld\n", last_rc);
544                         break;
545                 case 'r':
546                         len = atoi(commands+1);
547                         if (len <= 0)
548                                 len = 1;
549                         if (bufsize < len) {
550                                 void *tmp;
551                                 tmp = realloc(buf, len + ALIGN_LEN);
552                                 if (tmp == NULL) {
553                                         free(buf);
554                                         save_errno = errno;
555                                         perror("allocating buf for read\n");
556                                         exit(save_errno);
557                                 }
558                                 buf = tmp;
559                                 bufsize = len;
560                                 buf_align = (char *)((long)(buf + ALIGN_LEN) &
561                                                      ~ALIGN_LEN);
562                         }
563                         while (len > 0) {
564                                 rc = read(fd, buf_align, len);
565                                 if (rc == -1) {
566                                         save_errno = errno;
567                                         perror("read");
568                                         exit(save_errno);
569                                 }
570                                 if (rc < len) {
571                                         fprintf(stderr, "short read: %lld/%u\n",
572                                                 rc, len);
573                                         if (rc == 0)
574                                                 exit(ENODATA);
575                                 }
576                                 len -= rc;
577                                 if (verbose >= 2)
578                                         printf("%.*s\n", (int)rc, buf_align);
579                         }
580                         break;
581                 case 'R':
582                         for (i = 0; i < mmap_len && mmap_ptr; i += 4096)
583                                 junk += mmap_ptr[i];
584                         break;
585                 case 's':
586                         if (stat(fname, &st) == -1) {
587                                 save_errno = errno;
588                                 perror("stat");
589                                 exit(save_errno);
590                         }
591                         break;
592                 case 'S':
593                         if (fstat(fd, &st) == -1) {
594                                 save_errno = errno;
595                                 perror("fstat");
596                                 exit(save_errno);
597                         }
598                         break;
599                 case 't':
600                         if (fchmod(fd, 0) == -1) {
601                                 save_errno = errno;
602                                 perror("fchmod");
603                                 exit(save_errno);
604                         }
605                         break;
606                 case 'T':
607                         len = atoi(commands+1);
608                         if (ftruncate(fd, len) == -1) {
609                                 save_errno = errno;
610                                 printf("ftruncate (%d,%d)\n", fd, len);
611                                 perror("ftruncate");
612                                 exit(save_errno);
613                         }
614                         break;
615                 case 'u':
616                         if (unlink(fname) == -1) {
617                                 save_errno = errno;
618                                 perror("unlink");
619                                 exit(save_errno);
620                         }
621                         break;
622                 case 'U':
623                         if (munmap(mmap_ptr, mmap_len)) {
624                                 save_errno = errno;
625                                 perror("munmap");
626                                 exit(save_errno);
627                         }
628                         break;
629                 case 'v':
630                         verbose++;
631                         break;
632                 case 'V':
633                         len = get_flags(commands + 1, &flags);
634                         commands += len;
635                         len = -1; /* mdt index */
636                         if (commands[1] >= '0' && commands[1] <= '9')
637                                 len = atoi(commands+1);
638                         fd = llapi_create_volatile_idx(fname, len, flags);
639                         if (fd < 0) {
640                                 perror("llapi_create_volatile");
641                                 exit(fd);
642                         }
643                         rc = fd;
644                         break;
645                 case 'w':
646                         len = atoi(commands+1);
647                         if (len <= 0)
648                                 len = 1;
649                         if (bufsize < len) {
650                                 void *tmp;
651                                 tmp = realloc(buf, len + ALIGN_LEN);
652                                 if (tmp == NULL) {
653                                         free(buf);
654                                         save_errno = errno;
655                                         perror("allocating buf for write\n");
656                                         exit(save_errno);
657                                 }
658                                 buf = tmp;
659                                 bufsize = len;
660                                 buf_align = (char *)((long)(buf + ALIGN_LEN) &
661                                                      ~ALIGN_LEN);
662                                 strncpy(buf_align, msg, bufsize);
663                         }
664                         while (len > 0) {
665                                 rc = write(fd, buf_align, len);
666                                 if (rc == -1) {
667                                         save_errno = errno;
668                                         perror("write");
669                                         exit(save_errno);
670                                 }
671                                 if (rc < len)
672                                         fprintf(stderr, "short write: %lld/%u\n",
673                                                 rc, len);
674                                 len -= rc;
675                         }
676                         break;
677                 case 'W':
678                         for (i = 0; i < mmap_len && mmap_ptr; i += 4096)
679                                 mmap_ptr[i] += junk++;
680                         break;
681                 case 'x': {
682                         __u64 dv;
683
684                         rc = llapi_get_data_version(fd, &dv, 0);
685                         if (rc) {
686                                 fprintf(stderr, "cannot get file data version"
687                                         " %lld\n", rc);
688                                 exit(-rc);
689                         }
690                         printf("dataversion is %ju\n", (uintmax_t)dv);
691                         break;
692                 }
693                 case 'X': {
694                         __u32 layout_version;
695
696                         rc = llapi_get_ost_layout_version(fd, &layout_version);
697                         if (rc) {
698                                 fprintf(stderr, "cannot get ost layout version"
699                                         " %lld\n", rc);
700                                 exit(-rc);
701                         }
702                         printf("ostlayoutversion: %u\n", layout_version);
703                         break;
704                 }
705                 case 'y':
706                         if (fsync(fd) == -1) {
707                                 save_errno = errno;
708                                 perror("fsync");
709                                 exit(save_errno);
710                         }
711                         break;
712                 case 'Y':
713                         if (fdatasync(fd) == -1) {
714                                 save_errno = errno;
715                                 perror("fdatasync");
716                                 exit(save_errno);
717                         }
718                         break;
719                 case 'z': {
720                         off_t off;
721
722                         len = atoi(commands + 1);
723                         off = lseek(fd, len, SEEK_SET);
724                         if (off == (off_t)-1) {
725                                 save_errno = errno;
726                                 perror("lseek");
727                                 exit(save_errno);
728                         }
729
730                         rc = off;
731                         break;
732                 }
733                 case 'Z': {
734                         off_t off;
735
736                         len = atoi(commands + 1);
737                         off = lseek(fd, len, SEEK_CUR);
738                         if (off == (off_t)-1) {
739                                 save_errno = errno;
740                                 perror("lseek");
741                                 exit(save_errno);
742                         }
743
744                         rc = off;
745                         break;
746                 }
747                 case '-':
748                 case '0':
749                 case '1':
750                 case '2':
751                 case '3':
752                 case '4':
753                 case '5':
754                 case '6':
755                 case '7':
756                 case '8':
757                 case '9':
758                         break;
759                 default:
760                         fprintf(stderr, "unknown command \"%c\"\n", *commands);
761                         fprintf(stderr, usage, argv[0]);
762                         exit(1);
763                 }
764         }
765
766         if (buf)
767                 free(buf);
768
769         return 0;
770 }