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