Further rationalise our usage of assert()
[openafs.git] / src / venus / afsio.c
1 /*
2  * Copyright (c) 2007, Hartmut Reuter,
3  * RZG, Max-Planck-Institut f. Plasmaphysik.
4  * All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *   1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *   2. Redistributions in binary form must reproduce the above copyright
12  *      notice, this list of conditions and the following disclaimer in
13  *      the documentation and/or other materials provided with the
14  *      distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 /*
28  * Revised in 2010 by Chaz Chandler to enhance clientless operations.
29  * Now utilizes libafscp by Chaskiel Grundman.
30  * Work funded in part by Sine Nomine Associates (http://www.sinenomine.net/)
31  */
32
33 #include <afsconfig.h>
34 #include <afs/param.h>
35 #include <afs/stds.h>
36
37 #include <roken.h>
38
39 #ifdef AFS_NT40_ENV
40 #include <windows.h>
41 #define _CRT_RAND_S
42 #include <afs/smb_iocons.h>
43 #include <afs/afsd.h>
44 #include <afs/cm_ioctl.h>
45 #include <afs/pioctl_nt.h>
46 #include <WINNT/syscfg.h>
47 #else
48 #include <afs/afsint.h>
49 #define FSINT_COMMON_XG 1
50 #endif
51
52 #include <afs/opr.h>
53 #include <afs/cmd.h>
54 #include <afs/auth.h>
55 #include <afs/vlserver.h>
56 #include <afs/ihandle.h>
57 #include <afs/com_err.h>
58 #include <afs/afscp.h>
59
60 #ifdef HAVE_DIRECT_H
61 #include <direct.h>
62 #endif
63 #include <hcrypto/md5.h>
64 #ifdef AFS_PTHREAD_ENV
65 pthread_key_t uclient_key;
66 #endif
67
68 static int lockFile(struct cmd_syndesc *, void *);
69 static int readFile(struct cmd_syndesc *, void *);
70 static int writeFile(struct cmd_syndesc *, void *);
71 static void printDatarate(void);
72 static void summarizeDatarate(struct timeval *, const char *);
73 static int CmdProlog(struct cmd_syndesc *, char **, char **,
74                      char **, char **);
75 static int ScanFid(char *, struct AFSFid *);
76 static afs_int32 GetVenusFidByFid(char *, char *, int, struct afscp_venusfid **);
77 static afs_int32 GetVenusFidByPath(char *, char *, struct afscp_venusfid **);
78 static int BreakUpPath(char *, char *, char *, size_t);
79
80 static char pnp[AFSPATHMAX];    /* filename of this program when called */
81 static int verbose = 0;         /* Set if -verbose option given */
82 static int cellGiven = 0;       /* Set if -cell option given */
83 static int force = 0;           /* Set if -force option given */
84 static int readlock = 0;        /* Set if -readlock option given */
85 static int waittime = 0;        /* Set if -waittime option given */
86 static int useFid = 0;          /* Set if fidwrite/fidread/fidappend invoked */
87 static int append = 0;          /* Set if append/fidappend invoked */
88 static struct timeval starttime, opentime, readtime, writetime;
89 static afs_uint64 xfered = 0;
90 static struct timeval now;
91 #ifdef AFS_NT40_ENV
92 static int Timezone;            /* Roken gettimeofday ignores the timezone */
93 #else
94 static struct timezone Timezone;
95 #endif
96
97 #define BUFFLEN 65536
98 #define WRITEBUFLEN (BUFFLEN * 1024)
99 #define MEGABYTE_F 1048576.0f
100
101 static MD5_CTX md5;
102 static int md5sum = 0;          /* Set if -md5 option given */
103
104 struct wbuf {
105     struct wbuf *next;
106     afs_uint32 offset;          /* offset inside the buffer */
107     afs_uint32 buflen;          /* total length == BUFFLEN */
108     afs_uint32 used;            /* bytes used inside buffer */
109     char buf[BUFFLEN];
110 };
111
112 /*!
113  *  returns difference in seconds between two times
114  *
115  *  \param[in]  from    start time
116  *  \param[in]  to      end time
117  *
118  *  \post returns "to" minus "from" in seconds
119  *
120  */
121 static_inline float
122 time_elapsed(struct timeval *from, struct timeval *to)
123 {
124     return (float)(to->tv_sec + (to->tv_usec * 0.000001) - from->tv_sec -
125                    (from->tv_usec * 0.000001));
126 } /* time_elapsed */
127
128 /*!
129  * prints current average data transfer rate at no less than 30-second intervals
130  */
131 static void
132 printDatarate(void)
133 {
134     static float oldseconds = 0.0;
135     static afs_uint64 oldxfered = 0;
136     float seconds;
137
138     gettimeofday(&now, &Timezone);
139     seconds = time_elapsed(&opentime, &now);
140     if ((seconds - oldseconds) > 30) {
141         fprintf(stderr, "%llu MB transferred, present data rate = %.3f MB/sec.\n", xfered >> 20,        /* total bytes transferred, in MB */
142                 (xfered - oldxfered) / (seconds - oldseconds) / MEGABYTE_F);
143         oldxfered = xfered;
144         oldseconds = seconds;
145     }
146 } /* printDatarate */
147
148 /*!
149  * prints overall average data transfer rate and elapsed time
150  *
151  * \param[in]   tvp             current time (to compare with file open time)
152  * \param[in]   xfer_type       string identify transfer type ("read" or "write")
153  */
154 static void
155 summarizeDatarate(struct timeval *tvp, const char *xfer_type)
156 {
157     float seconds = time_elapsed(&opentime, tvp);
158
159     fprintf(stderr, "Transfer of %llu bytes took %.3f sec.\n",
160             xfered, seconds);
161     fprintf(stderr, "Total data rate = %.03f MB/sec. for %s\n",
162             xfered / seconds / MEGABYTE_F, xfer_type);
163 } /* summarizeDatarate */
164
165 /*!
166  * prints final MD5 sum of all file data transferred
167  *
168  * \param[in]   fname   file name or FID
169  */
170 static void
171 summarizeMD5(char *fname)
172 {
173     afs_uint32 md5int[4];
174     char *p;
175
176     MD5_Final((char *) &md5int[0], &md5);
177     p = fname + strlen(fname);
178     while (p > fname) {
179         if (*(--p) == '/') {
180             ++p;
181             break;
182         }
183     }
184     fprintf(stderr, "%08x%08x%08x%08x  %s\n", htonl(md5int[0]),
185             htonl(md5int[1]), htonl(md5int[2]), htonl(md5int[3]), p);
186 } /* summarizeMD5 */
187
188 /*!
189  * parses all command-line arguments
190  *
191  * \param[in]  as       arguments list
192  * \param[out] cellp    cell name
193  * \param[out] realmp   realm name
194  * \param[out] fnp      filename (either fid or path)
195  * \param[out] slp      "synthesized" (made up) data given
196  *
197  * \post returns 0 on success or -1 on error
198  *
199  */
200 static int
201 CmdProlog(struct cmd_syndesc *as, char **cellp, char **realmp,
202           char **fnp, char **slp)
203 {
204     int i;
205     struct cmd_parmdesc *pdp;
206
207     if (as == NULL) {
208         afs_com_err(pnp, EINVAL, "(syndesc is null)");
209         return -1;
210     }
211
212     /* determine which command was requested */
213     if (strncmp(as->name, "fid", 3) == 0) /* fidread/fidwrite/fidappend */
214         useFid = 1;
215     if ( (strcmp(as->name, "append") == 0) ||
216          (strcmp(as->name, "fidappend") == 0) )
217         append = 1;             /* global */
218
219     /* attempts to ensure loop is bounded: */
220     for (pdp = as->parms, i = 0; pdp && (i < as->nParms); i++, pdp++) {
221         if (pdp->items != NULL) {
222             if (strcmp(pdp->name, "-verbose") == 0)
223                 verbose = 1;
224             else if (strcmp(pdp->name, "-md5") == 0)
225                 md5sum = 1;     /* global */
226             else if (strcmp(pdp->name, "-cell") == 0) {
227                 cellGiven = 1;  /* global */
228                 *cellp = pdp->items->data;
229             } else if ( (strcmp(pdp->name, "-file") == 0) ||
230                         (strcmp(pdp->name, "-fid") == 0) ||
231                         (strcmp(pdp->name, "-vnode") == 0) )
232                 *fnp = pdp->items->data;
233             else if (strcmp(pdp->name, "-force") == 0)
234                 force = 1;      /* global */
235             else if (strcmp(pdp->name, "-synthesize") == 0)
236                 *slp = pdp->items->data;
237             else if (strcmp(pdp->name, "-realm") == 0)
238                 *realmp = pdp->items->data;
239             else if (strcmp(pdp->name, "-wait") == 0)
240                 waittime = atoi(pdp->items->data);
241             else if (strcmp(pdp->name, "-readlock") == 0)
242                 readlock = 1;
243         }
244     }
245     return 0;
246 }                               /* CmdProlog */
247
248 int
249 main(int argc, char **argv)
250 {
251     struct cmd_syndesc *ts;
252     char baseName[AFSNAMEMAX];
253
254     /* try to get only the base name of this executable for use in logs */
255     if (BreakUpPath(argv[0], NULL, baseName, AFSNAMEMAX) > 0)
256         strlcpy(pnp, baseName, AFSNAMEMAX);
257     else
258         strlcpy(pnp, argv[0], AFSPATHMAX);
259
260 #ifdef AFS_PTHREAD_ENV
261     opr_Verify(pthread_key_create(&uclient_key, NULL) == 0);
262 #endif
263
264     ts = cmd_CreateSyntax("lock", lockFile, (void *)LockWrite,
265                           "lock a file in AFS");
266     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_REQUIRED, "AFS-filename");
267     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
268     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *)0);
269     cmd_Seek(ts, 4);
270     cmd_AddParm(ts, "-realm", CMD_SINGLE, CMD_OPTIONAL, "REALMNAME");
271     cmd_AddParm(ts, "-waitseconds", CMD_SINGLE, CMD_OPTIONAL, "seconds to wait before giving up");
272     cmd_AddParm(ts, "-readlock", CMD_FLAG, CMD_OPTIONAL, "read lock only");
273
274     ts = cmd_CreateSyntax("fidlock", lockFile, (void *)LockWrite,
275                           "lock by FID a file from AFS");
276     cmd_IsAdministratorCommand(ts);
277     cmd_AddParm(ts, "-fid", CMD_SINGLE, CMD_REQUIRED,
278                 "volume.vnode.uniquifier");
279     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
280     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *)0);
281     cmd_Seek(ts, 4);
282     cmd_AddParm(ts, "-realm", CMD_SINGLE, CMD_OPTIONAL, "REALMNAME");
283     cmd_AddParm(ts, "-waitseconds", CMD_SINGLE, CMD_OPTIONAL, "seconds to wait before giving up");
284     cmd_AddParm(ts, "-readlock", CMD_FLAG, CMD_OPTIONAL, "read lock only");
285
286     ts = cmd_CreateSyntax("unlock", lockFile, (void *)LockRelease,
287                           "unlock a file in AFS");
288     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_REQUIRED, "AFS-filename");
289     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
290     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *)0);
291     cmd_Seek(ts, 4);
292     cmd_AddParm(ts, "-realm", CMD_SINGLE, CMD_OPTIONAL, "REALMNAME");
293     cmd_AddParm(ts, "-waitseconds", CMD_SINGLE, CMD_OPTIONAL, "seconds to wait before giving up");
294
295     ts = cmd_CreateSyntax("fidunlock", lockFile, (void *)LockRelease,
296                           "unlock by FID a file from AFS");
297     cmd_IsAdministratorCommand(ts);
298     cmd_AddParm(ts, "-fid", CMD_SINGLE, CMD_REQUIRED,
299                 "volume.vnode.uniquifier");
300     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
301     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *)0);
302     cmd_Seek(ts, 4);
303     cmd_AddParm(ts, "-realm", CMD_SINGLE, CMD_OPTIONAL, "REALMNAME");
304     cmd_AddParm(ts, "-waitseconds", CMD_SINGLE, CMD_OPTIONAL, "seconds to wait before giving up");
305
306     ts = cmd_CreateSyntax("read", readFile, NULL,
307                           "read a file from AFS");
308     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_REQUIRED, "AFS-filename");
309     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
310     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *)0);
311     cmd_AddParm(ts, "-md5", CMD_FLAG, CMD_OPTIONAL, "calculate md5 checksum");
312     cmd_AddParm(ts, "-realm", CMD_SINGLE, CMD_OPTIONAL, "REALMNAME");
313
314     ts = cmd_CreateSyntax("fidread", readFile, CMD_REQUIRED,
315                           "read on a non AFS-client a file from AFS");
316     cmd_IsAdministratorCommand(ts);
317     cmd_AddParm(ts, "-fid", CMD_SINGLE, CMD_REQUIRED,
318                 "volume.vnode.uniquifier");
319     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
320     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *)0);
321     cmd_AddParm(ts, "-md5", CMD_FLAG, CMD_OPTIONAL, "calculate md5 checksum");
322     cmd_AddParm(ts, "-realm", CMD_SINGLE, CMD_OPTIONAL, "REALMNAME");
323
324     ts = cmd_CreateSyntax("write", writeFile, NULL,
325                           "write a file into AFS");
326     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_REQUIRED, "AFS-filename");
327     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
328     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *)0);
329     cmd_AddParm(ts, "-md5", CMD_FLAG, CMD_OPTIONAL, "calculate md5 checksum");
330     cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL,
331                 "overwrite existing file");
332     cmd_AddParm(ts, "-synthesize", CMD_SINGLE, CMD_OPTIONAL,
333                 "create data pattern of specified length instead reading from stdin");
334     cmd_AddParm(ts, "-realm", CMD_SINGLE, CMD_OPTIONAL, "REALMNAME");
335
336     ts = cmd_CreateSyntax("fidwrite", writeFile, CMD_REQUIRED,
337                           "write a file into AFS");
338     cmd_IsAdministratorCommand(ts);
339     cmd_AddParm(ts, "-vnode", CMD_SINGLE, CMD_REQUIRED,
340                 "volume.vnode.uniquifier");
341     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
342     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *)0);
343     cmd_AddParm(ts, "-md5", CMD_FLAG, CMD_OPTIONAL, "calculate md5 checksum");
344     cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL,
345                 "overwrite existing file");
346     cmd_AddParm(ts, "-realm", CMD_SINGLE, CMD_OPTIONAL, "REALMNAME");
347
348     ts = cmd_CreateSyntax("append", writeFile, NULL,
349                           "append to a file in AFS");
350     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_REQUIRED, "AFS-filename");
351     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
352     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *)0);
353     cmd_AddParm(ts, "-realm", CMD_SINGLE, CMD_OPTIONAL, "REALMNAME");
354
355     ts = cmd_CreateSyntax("fidappend", writeFile, NULL,
356                           "append to a file in AFS");
357     cmd_IsAdministratorCommand(ts);
358     cmd_AddParm(ts, "-vnode", CMD_SINGLE, CMD_REQUIRED,
359                 "volume.vnode.uniquifier");
360     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cellname");
361     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, (char *)0);
362     cmd_AddParm(ts, "-realm", CMD_SINGLE, CMD_OPTIONAL, "REALMNAME");
363
364     if (afscp_Init(NULL) != 0)
365         exit(1);
366
367     cmd_Dispatch(argc, argv);
368
369     afscp_Finalize();
370     exit(0);
371 } /* main */
372
373 /*!
374  * standardized way of parsing a File ID (FID) from command line input
375  *
376  * \param[in]   fidString       dot-delimited FID triple
377  * \param[out]  fid             pointer to the AFSFid to fill in
378  *
379  * \post The FID pointed to by "fid" is filled in which the parsed Volume,
380  *       Vnode, and Uniquifier data.  The string should be in the format
381  *       of three numbers separated by dot (.) delimiters, representing
382  *       (in order) the volume id, vnode number, and uniquifier.
383  *       Example: "576821346.1.1"
384  */
385 static int
386 ScanFid(char *fidString, struct AFSFid *fid)
387 {
388     int i = 0, code = 0;
389     long unsigned int f1, f2, f3;
390
391     if (fidString) {
392         i = sscanf(fidString, "%lu.%lu.%lu", &f1, &f2, &f3);
393         fid->Volume = (afs_uint32) f1;
394         fid->Vnode = (afs_uint32) f2;
395         fid->Unique = (afs_uint32) f3;
396     }
397     if (i != 3) {
398         fid->Volume = 0;
399         fid->Vnode = 0;
400         fid->Unique = 0;
401         code = EINVAL;
402         afs_com_err(pnp, code, "(invalid FID triple: %s)", fidString);
403     }
404
405     return code;
406 } /* ScanFid */
407
408 /*!
409  * look up cell info and verify FID info from user input
410  *
411  * \param[in]   fidString       string containing FID info
412  * \param[in]   cellName        cell name string
413  * \param[in]   onlyRW          bool: 1 = RW vol only, 0 = any vol type
414  * \param[out]  avfpp           pointer to venusfid info
415  *
416  * \post *avfpp will contain the VenusFid info found for the FID
417  *       given by the used in the string fidString and zero is
418  *       returned.  If not found, an appropriate afs error code
419  *       is returned and *avfpp will be NULL.
420  *
421  * \note Any non-NULL avfpp returned should later be freed with
422  *       afscp_FreeFid() when no longer needed.
423  */
424 static afs_int32
425 GetVenusFidByFid(char *fidString, char *cellName, int onlyRW,
426                  struct afscp_venusfid **avfpp)
427 {
428     afs_int32 code = 0;
429     struct stat sbuf;
430     struct afscp_volume *avolp;
431
432     if (*avfpp == NULL) {
433         *avfpp = calloc(1, sizeof(struct afscp_venusfid));
434         if ( *avfpp == NULL ) {
435             code = ENOMEM;
436             return code;
437         }
438     }
439
440     if (cellName == NULL) {
441         (*avfpp)->cell = afscp_DefaultCell();
442     } else {
443         (*avfpp)->cell = afscp_CellByName(cellName, NULL);
444     }
445     if ((*avfpp)->cell == NULL) {
446         if (afscp_errno == 0)
447             code = EINVAL;
448         else
449             code = afscp_errno;
450         return code;
451     }
452
453     code = ScanFid(fidString, &((*avfpp)->fid));
454     if (code != 0) {
455         code = EINVAL;
456         return code;
457     }
458
459     avolp = afscp_VolumeById((*avfpp)->cell, (*avfpp)->fid.Volume);
460     if (avolp == NULL) {
461         if (afscp_errno == 0)
462             code = ENOENT;
463         else
464             code = afscp_errno;
465         afs_com_err(pnp, code, "(finding volume %lu)",
466                     afs_printable_uint32_lu((*avfpp)->fid.Volume));
467         return code;
468     }
469
470     if ( onlyRW && (avolp->voltype != RWVOL) ) {
471         avolp = afscp_VolumeByName((*avfpp)->cell, avolp->name, RWVOL);
472         if (avolp == NULL) {
473             if (afscp_errno == 0)
474                 code = ENOENT;
475             else
476                 code = afscp_errno;
477             afs_com_err(pnp, code, "(finding volume %lu)",
478                         afs_printable_uint32_lu((*avfpp)->fid.Volume));
479             return code;
480         }
481         (*avfpp)->fid.Volume = avolp->id; /* is this safe? */
482     }
483
484     code = afscp_Stat((*avfpp), &sbuf);
485     if (code != 0) {
486         afs_com_err(pnp, code, "(stat failed with code %d)", code);
487         return code;
488     }
489     return 0;
490 } /* GetVenusFidByFid */
491
492 /*!
493  * Split a full path up into dirName and baseName components
494  *
495  * \param[in]   fullPath        can be absolute, relative, or local
496  * \param[out]  dirName         pointer to allocated char buffer or NULL
497  * \param[out]  baseName        pointer to allocated char buffer or NULL
498  *
499  * \post To the fulleset extent possible, the rightmost full path
500  *       component will be copied into baseName and all other
501  *       components into dirName (minus the trailing path separator).
502  *       If either dirName or baseName are NULL, only the non-NULL
503  *       pointer will be filled in (but both can't be null or it would
504  *       be pointless) -- so the caller can retrieve, say, only baseName
505  *       if desired.  The return code is the number of strings copied:
506  *       0 if neither dirName nor baseName could be filled in
507  *       1 if either dirName or baseName were filled in
508  *       2 if both dirName and baseName were filled in
509  */
510 static int
511 BreakUpPath(char *fullPath, char *dirName, char *baseName, size_t baseNameSize)
512 {
513     char *lastSlash;
514     size_t dirNameLen = 0;
515     int code = 0, useDirName = 1, useBaseName = 1;
516
517     if (fullPath == NULL) {
518         return code;
519     }
520
521     if (dirName == NULL)
522         useDirName = 0;
523     if (baseName == NULL)
524         useBaseName = 0;
525     if (!useBaseName && !useDirName) {
526         /* would be pointless to continue -- must be error in call */
527         return code;
528     }
529 #ifdef AFS_NT40_ENV
530     lastSlash = strrchr(fullPath, '\\');
531 #else
532     lastSlash = strrchr(fullPath, '/');
533 #endif
534     if (lastSlash != NULL) {
535         /* then lastSlash points to the last path separator in fullPath */
536         if (useDirName) {
537             dirNameLen = strlen(fullPath) - strlen(lastSlash);
538             strlcpy(dirName, fullPath, min(dirNameLen + 1, baseNameSize));
539             code++;
540         }
541         if (useBaseName) {
542             lastSlash++;
543             strlcpy(baseName, lastSlash, min(strlen(lastSlash) + 1, baseNameSize));
544             code++;
545         }
546     } else {
547         /* there are no path separators in fullPath -- it's just a baseName */
548         if (useBaseName) {
549             strlcpy(baseName, fullPath, min(strlen(fullPath) + 1, baseNameSize));
550             code++;
551         }
552     }
553     return code;
554 } /* BreakUpPath */
555
556 /*!
557  * Get the VenusFid info available for the file at AFS path 'fullPath'.
558  * Works without pioctls/afsd by using libafscp.  Analogous to
559  * get_file_cell() in the previous iteration of afsio.
560  *
561  * \param[in]   fullPath        the file name
562  * \param[in]   cellName        the cell name to look up
563  * \param[out]  avfpp           pointer to Venus FID info to be filled in
564  *
565  * \post If the path resolves successfully (using afscp_ResolvePath),
566  *       then vfpp will contain the Venus FID info (cell info plus
567  *       AFSFid) of the last path segment in fullPath.
568  */
569 static afs_int32
570 GetVenusFidByPath(char *fullPath, char *cellName,
571                   struct afscp_venusfid **avfpp)
572 {
573     afs_int32 code = 0;
574
575     if (fullPath == NULL) {
576         return -1;
577     }
578
579     if (cellName != NULL) {
580         code = (afs_int32) afscp_SetDefaultCell(cellName);
581         if (code != 0) {
582             return code;
583         }
584     }
585
586     *avfpp = afscp_ResolvePath(fullPath);
587     if (*avfpp == NULL) {
588         if (afscp_errno == 0)
589             code = ENOENT;
590         else
591             code = afscp_errno;
592     }
593
594     return code;
595 } /* GetVenusFidByPath */
596
597 static int
598 lockFile(struct cmd_syndesc *as, void *arock)
599 {
600     char *fname = NULL;
601     char *cell = NULL;
602     char *realm = NULL;
603     afs_int32 code = 0;
604     struct AFSFetchStatus OutStatus;
605     struct afscp_venusfid *avfp = NULL;
606     char *buf = 0;
607     char ipv4_addr[16];
608     int locktype = (int)(intptr_t) arock;
609
610 #ifdef AFS_NT40_ENV
611     /* stdout on Windows defaults to _O_TEXT mode */
612     _setmode(1, _O_BINARY);
613 #endif
614
615     gettimeofday(&starttime, &Timezone);
616
617     CmdProlog(as, &cell, &realm, &fname, NULL);
618     afscp_AnonymousAuth(1);
619
620     if ((locktype == LockWrite) && readlock)
621         locktype = LockRead;
622
623     if (realm != NULL)
624         code = afscp_SetDefaultRealm(realm);
625
626     if (cell != NULL)
627         code = afscp_SetDefaultCell(cell);
628
629     if (useFid)
630         code = GetVenusFidByFid(fname, cell, 0, &avfp);
631     else
632         code = GetVenusFidByPath(fname, cell, &avfp);
633     if (code != 0) {
634         afs_com_err(pnp, code, "(file not found: %s)", fname);
635         return code;
636     }
637
638 retry:
639     code = afscp_GetStatus(avfp, &OutStatus);
640     if (code != 0) {
641         afs_inet_ntoa_r(avfp->cell->fsservers[0]->addrs[0], ipv4_addr);
642         afs_com_err(pnp, code, "(failed to get status of file %s from"
643                     "server %s, code = %d)", fname, ipv4_addr, code);
644         afscp_FreeFid(avfp);
645         return code;
646     }
647
648     if (locktype != LockRelease) {
649         while (OutStatus.lockCount != 0) {
650             code = afscp_WaitForCallback(avfp, waittime);
651             if ((code == -1) && (afscp_errno == ETIMEDOUT))
652                 break;
653             if ((code = afscp_GetStatus(avfp, &OutStatus)) != 0)
654                 break;
655         }
656     } else {
657         if (OutStatus.lockCount == 0) {
658             code = -1;
659         }
660     }
661
662     if (!code) {
663         code = afscp_Lock(avfp, locktype);
664         if ((code == -1) && (afscp_errno == EWOULDBLOCK))
665             goto retry;
666     }
667     afscp_FreeFid(avfp);
668
669     if (buf != NULL)
670         free(buf);
671
672     if (code != 0)
673         afs_com_err(pnp, code, "(failed to change lock status: %d)", afscp_errno);
674
675     return code;
676 } /* lockFile */
677
678 static int
679 readFile(struct cmd_syndesc *as, void *unused)
680 {
681     char *fname = NULL;
682     char *cell = NULL;
683     char *realm = NULL;
684     afs_int32 code = 0;
685     struct AFSFetchStatus OutStatus;
686     struct afscp_venusfid *avfp = NULL;
687     afs_int64 Pos;
688     afs_int32 len;
689     afs_int64 length = 0, Len;
690     int bytes;
691     int worstCode = 0;
692     char *buf = 0;
693     char ipv4_addr[16];
694     int bufflen = BUFFLEN;
695
696 #ifdef AFS_NT40_ENV
697     /* stdout on Windows defaults to _O_TEXT mode */
698     _setmode(1, _O_BINARY);
699 #endif
700
701     gettimeofday(&starttime, &Timezone);
702
703     CmdProlog(as, &cell, &realm, &fname, NULL);
704     afscp_AnonymousAuth(1);
705
706     if (md5sum)
707         MD5_Init(&md5);
708
709     if (realm != NULL)
710         code = afscp_SetDefaultRealm(realm);
711
712     if (cell != NULL)
713         code = afscp_SetDefaultCell(cell);
714
715     if (useFid)
716         code = GetVenusFidByFid(fname, cell, 0, &avfp);
717     else
718         code = GetVenusFidByPath(fname, cell, &avfp);
719     if (code != 0) {
720         afs_com_err(pnp, code, "(file not found: %s)", fname);
721         return code;
722     }
723
724     if (avfp->fid.Vnode & 1) {
725         code = ENOENT;
726         afs_com_err(pnp, code, "(%s is a directory, not a file)", fname);
727         afscp_FreeFid(avfp);
728         return code;
729     }
730
731     code = afscp_GetStatus(avfp, &OutStatus);
732     if (code != 0) {
733         afs_inet_ntoa_r(avfp->cell->fsservers[0]->addrs[0], ipv4_addr);
734         afs_com_err(pnp, code, "(failed to get status of file %s from"
735                     "server %s, code = %d)", fname, ipv4_addr, code);
736         afscp_FreeFid(avfp);
737         return code;
738     }
739
740     gettimeofday(&opentime, &Timezone);
741     if (verbose)
742         fprintf(stderr, "Startup to find the file took %.3f sec.\n",
743                 time_elapsed(&starttime, &opentime));
744     Len = OutStatus.Length_hi;
745     Len <<= 32;
746     Len += OutStatus.Length;
747     ZeroInt64(Pos);
748     buf = calloc(bufflen, sizeof(char));
749     if (buf == NULL) {
750         code = ENOMEM;
751         afs_com_err(pnp, code, "(cannot allocate buffer)");
752         afscp_FreeFid(avfp);
753         return code;
754     }
755     length = Len;
756     while (!code && NonZeroInt64(length)) {
757         if (length > bufflen)
758             len = bufflen;
759         else
760             len = (afs_int32) length;
761         bytes = afscp_PRead(avfp, buf, len, Pos);
762         if (bytes != len)
763             code = -3; /* what error name should we use here? */
764         if (md5sum)
765             MD5_Update(&md5, buf, len);
766         if (code == 0) {
767             len = write(1, buf, len); /* to stdout */
768             if (len == 0)
769                 code = errno;
770         }
771         length -= len;
772         xfered += len;
773         if (verbose)
774             printDatarate();
775         Pos += len;
776         worstCode = code;
777     }
778     afscp_FreeFid(avfp);
779
780     gettimeofday(&readtime, &Timezone);
781     if (md5sum)
782         summarizeMD5(fname);
783     if (verbose)
784         summarizeDatarate(&readtime, "read");
785     if (buf != NULL)
786         free(buf);
787
788     return worstCode;
789 } /* readFile */
790
791 static int
792 writeFile(struct cmd_syndesc *as, void *unused)
793 {
794     char *fname = NULL;
795     char *cell = NULL;
796     char *sSynthLen = NULL;
797     char *realm = NULL;
798     afs_int32 code = 0;
799     afs_int32 byteswritten;
800     struct AFSFetchStatus OutStatus;
801     struct AFSStoreStatus InStatus;
802     struct afscp_venusfid *dirvfp = NULL, *newvfp = NULL;
803     afs_int64 Pos;
804     afs_int64 length, Len, synthlength = 0, offset = 0;
805     afs_int64 bytes;
806     int worstCode = 0;
807     int synthesize = 0;
808     int overWrite = 0;
809     struct wbuf *bufchain = 0;
810     struct wbuf *previous, *tbuf;
811     char dirName[AFSPATHMAX];
812     char baseName[AFSNAMEMAX];
813     char ipv4_addr[16];
814
815 #ifdef AFS_NT40_ENV
816     /* stdin on Windows defaults to _O_TEXT mode */
817     _setmode(0, _O_BINARY);
818 #endif
819
820     CmdProlog(as, &cell, &realm, &fname, &sSynthLen);
821     afscp_AnonymousAuth(1);
822
823     if (realm != NULL)
824         code = afscp_SetDefaultRealm(realm);
825
826     if (cell != NULL)
827         code = afscp_SetDefaultCell(cell);
828
829     if (sSynthLen) {
830         code = util_GetInt64(sSynthLen, &synthlength);
831         if (code != 0) {
832             afs_com_err(pnp, code, "(invalid value for synthesize length %s)",
833                         sSynthLen);
834             return code;
835         }
836         synthesize = 1;
837     }
838
839     if (useFid) {
840         code = GetVenusFidByFid(fname, cell, 1, &newvfp);
841         if (code != 0) {
842             afs_com_err(pnp, code, "(GetVenusFidByFid returned code %d)", code);
843             return code;
844         }
845     } else {
846         code = GetVenusFidByPath(fname, cell, &newvfp);
847         if (code == 0) { /* file was found */
848             if (force)
849                 overWrite = 1;
850             else if (!append) {
851                 /*
852                  * file cannot already exist if specified by path and not
853                  * appending to it unless user forces overwrite
854                  */
855                 code = EEXIST;
856                 afscp_FreeFid(newvfp);
857                 afs_com_err(pnp, code, "(use -force to overwrite)");
858                 return code;
859             }
860         } else { /* file not found */
861             if (append) {
862                 code = ENOENT;
863                 afs_com_err(pnp, code, "(cannot append to non-existent file)");
864                 return code;
865             }
866         }
867         if (!append && !overWrite) { /* must create a new file in this case */
868             if ( BreakUpPath(fname, dirName, baseName, AFSNAMEMAX) != 2 ) {
869                 code = EINVAL;
870                 afs_com_err(pnp, code, "(must provide full AFS path)");
871                 afscp_FreeFid(newvfp);
872                 return code;
873             }
874
875             code = GetVenusFidByPath(dirName, cell, &dirvfp);
876             afscp_FreeFid(newvfp); /* release now-unneeded fid */
877             newvfp = NULL;
878             if (code != 0) {
879                 afs_com_err(pnp, code, "(is dir %s in AFS?)", dirName);
880                 return code;
881             }
882         }
883     }
884
885     if ( (newvfp != NULL) && (newvfp->fid.Vnode & 1) ) {
886         code = EISDIR;
887         afs_com_err(pnp, code, "(%s is a directory, not a file)", fname);
888         afscp_FreeFid(newvfp);
889         afscp_FreeFid(dirvfp);
890         return code;
891     }
892     gettimeofday(&starttime, &Timezone);
893
894     InStatus.UnixModeBits = 0644;
895     InStatus.Mask = AFS_SETMODE + AFS_FSYNC;
896     if (newvfp == NULL) {
897         code = afscp_CreateFile(dirvfp, baseName, &InStatus, &newvfp);
898         if (code != 0) {
899             afs_com_err(pnp, code,
900                         "(could not create file %s in directory %lu.%lu.%lu)",
901                         baseName, afs_printable_uint32_lu(dirvfp->fid.Volume),
902                         afs_printable_uint32_lu(dirvfp->fid.Vnode),
903                         afs_printable_uint32_lu(dirvfp->fid.Unique));
904             return code;
905         }
906     }
907     code = afscp_GetStatus(newvfp, &OutStatus);
908     if (code != 0) {
909         afs_inet_ntoa_r(newvfp->cell->fsservers[0]->addrs[0], ipv4_addr);
910         afs_com_err(pnp, code, "(failed to get status of file %s from"
911                     "server %s, code = %d)", fname, ipv4_addr, code);
912         afscp_FreeFid(newvfp);
913         afscp_FreeFid(dirvfp);
914         return code;
915     }
916
917     if ( !append && !force &&
918          (OutStatus.Length != 0 || OutStatus.Length_hi !=0 ) ) {
919         /*
920          * file exists, is of non-zero length, and we're not appending
921          * to it: user must force overwrite
922          * (covers fidwrite edge case)
923          */
924         code = EEXIST;
925         afscp_FreeFid(newvfp);
926         afscp_FreeFid(dirvfp);
927         afs_com_err(pnp, code, "(use -force to overwrite)");
928         return code;
929     }
930
931     if (append) {
932         Pos = OutStatus.Length_hi;
933         Pos = (Pos << 32) | OutStatus.Length;
934     } else
935         Pos = 0;
936     previous = (struct wbuf *)&bufchain;
937     if (md5sum)
938         MD5_Init(&md5);
939
940     /*
941      * currently, these two while loops (1) read the whole source file in
942      * before (2) writing any of it out, meaning that afsio can't deal with
943      * files larger than the maximum amount of memory designated for
944      * reading a file in (WRITEBUFLEN).
945      * Consider going to a single loop, like in readFile(), though will
946      * have implications on timing statistics (such as the "Startup to
947      * find the file" time, below).
948      */
949     Len = 0;
950     while (Len < WRITEBUFLEN) {
951         tbuf = calloc(1, sizeof(struct wbuf));
952         if (tbuf == NULL) {
953             if (!bufchain) {
954                 code = ENOMEM;
955                 afscp_FreeFid(newvfp);
956                 afscp_FreeFid(dirvfp);
957                 afs_com_err(pnp, code, "(cannot allocate buffer)");
958                 return code;
959             }
960             break;
961         }
962         tbuf->buflen = BUFFLEN;
963         if (synthesize) {
964             afs_int64 ll, l = tbuf->buflen;
965             if (l > synthlength)
966                 l = synthlength;
967             for (ll = 0; ll < l; ll += 4096) {
968                 sprintf(&tbuf->buf[ll], "Offset (0x%x, 0x%x)\n",
969                         (unsigned int)((offset + ll) >> 32),
970                         (unsigned int)((offset + ll) & 0xffffffff));
971             }
972             offset += l;
973             synthlength -= l;
974             tbuf->used = (afs_int32) l;
975         } else
976             tbuf->used = read(0, &tbuf->buf, tbuf->buflen); /* from stdin */
977         if (tbuf->used == 0) {
978             free(tbuf);
979             break;
980         }
981         if (md5sum)
982             MD5_Update(&md5, &tbuf->buf, tbuf->used);
983         previous->next = tbuf;
984         previous = tbuf;
985         Len += tbuf->used;
986     }
987     gettimeofday(&opentime, &Timezone);
988     if (verbose)
989         fprintf(stderr, "Startup to find the file took %.3f sec.\n",
990                 time_elapsed(&starttime, &opentime));
991     bytes = Len;
992     while (!code && bytes) {
993         Len = bytes;
994         length = Len;
995         tbuf = bufchain;
996         if (Len) {
997             for (tbuf = bufchain; tbuf; tbuf = tbuf->next) {
998                 if (tbuf->used == 0)
999                     break;
1000                 byteswritten = afscp_PWrite(newvfp, tbuf->buf,
1001                                             tbuf->used, Pos + xfered);
1002                 if (byteswritten != tbuf->used) {
1003                     fprintf(stderr,"Only %d instead of %" AFS_INT64_FMT " bytes transferred by rx_Write()\n", byteswritten, length);
1004                     fprintf(stderr, "At %" AFS_UINT64_FMT " bytes from the end\n", length);
1005                     code = -4;
1006                     break;
1007                 }
1008                 xfered += tbuf->used;
1009                 if (verbose)
1010                     printDatarate();
1011                 length -= tbuf->used;
1012             }
1013         }
1014         Pos += Len;
1015         bytes = 0;
1016         if (!code) {
1017             for (tbuf = bufchain; tbuf; tbuf = tbuf->next) {
1018                 tbuf->offset = 0;
1019                 if (synthesize) {
1020                     afs_int64 ll, l = tbuf->buflen;
1021                     if (l > synthlength)
1022                         l = synthlength;
1023                     for (ll = 0; ll < l; ll += 4096) {
1024                         sprintf(&tbuf->buf[ll], "Offset (0x%x, 0x%x)\n",
1025                                 (unsigned int)((offset + ll) >> 32),
1026                                 (unsigned int)((offset + ll) & 0xffffffff));
1027                     }
1028                     offset += l;
1029                     synthlength -= l;
1030                     tbuf->used = (afs_int32) l;
1031                 } else
1032                     tbuf->used = read(0, &tbuf->buf, tbuf->buflen); /* from stdin */
1033                 if (!tbuf->used)
1034                     break;
1035                 if (md5sum)
1036                     MD5_Update(&md5, &tbuf->buf, tbuf->used);
1037                 Len += tbuf->used;
1038                 bytes += tbuf->used;
1039             }
1040         }
1041     }
1042     afscp_FreeFid(newvfp);
1043     afscp_FreeFid(dirvfp);
1044
1045     gettimeofday(&writetime, &Timezone);
1046     if (code) {
1047         afs_com_err(pnp, code, "(%s failed with code %d)", as->name,
1048                     code);
1049     } else if (verbose) {
1050         summarizeDatarate(&writetime, "write");
1051     }
1052     while (bufchain) {
1053         tbuf = bufchain;
1054         bufchain = tbuf->next;
1055         free(tbuf);
1056     }
1057
1058     if (md5sum)
1059         summarizeMD5(fname);
1060
1061     return worstCode;
1062 } /* writeFile */