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