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