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