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