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