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