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