VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/tar.cpp@ 30124

Last change on this file since 30124 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.1 KB
Line 
1/* $Id: tar.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
2/** @file
3 * IPRT - Tar archive I/O.
4 */
5
6/*
7 * Copyright (C) 2009 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/tar.h>
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/err.h>
37#include <iprt/file.h>
38#include <iprt/mem.h>
39#include <iprt/path.h>
40#include <iprt/string.h>
41
42
43/*******************************************************************************
44* Structures and Typedefs *
45*******************************************************************************/
46
47/** @name RTTARRECORD::h::linkflag
48 * @{ */
49#define LF_OLDNORMAL '\0' /**< Normal disk file, Unix compatible */
50#define LF_NORMAL '0' /**< Normal disk file */
51#define LF_LINK '1' /**< Link to previously dumped file */
52#define LF_SYMLINK '2' /**< Symbolic link */
53#define LF_CHR '3' /**< Character special file */
54#define LF_BLK '4' /**< Block special file */
55#define LF_DIR '5' /**< Directory */
56#define LF_FIFO '6' /**< FIFO special file */
57#define LF_CONTIG '7' /**< Contiguous file */
58/** @} */
59
60typedef union RTTARRECORD
61{
62 char d[512];
63 struct h
64 {
65 char name[100];
66 char mode[8];
67 char uid[8];
68 char gid[8];
69 char size[12];
70 char mtime[12];
71 char chksum[8];
72 char linkflag;
73 char linkname[100];
74 char magic[8];
75 char uname[32];
76 char gname[32];
77 char devmajor[8];
78 char devminor[8];
79 } h;
80} RTTARRECORD;
81typedef RTTARRECORD *PRTTARRECORD;
82AssertCompileSize(RTTARRECORD, 512);
83AssertCompileMemberOffset(RTTARRECORD, h.size, 100+8*3);
84
85#if 0 /* not currently used */
86typedef struct RTTARFILELIST
87{
88 char *pszFilename;
89 RTTARFILELIST *pNext;
90} RTTARFILELIST;
91typedef RTTARFILELIST *PRTTARFILELIST;
92#endif
93
94
95/*******************************************************************************
96* Internal Functions *
97*******************************************************************************/
98
99static int rtTarCalcChkSum(PRTTARRECORD pRecord, uint32_t *pChkSum)
100{
101 uint32_t check = 0;
102 uint32_t zero = 0;
103 for (size_t i = 0; i < sizeof(RTTARRECORD); ++i)
104 {
105 /* Calculate the sum of every byte from the header. The checksum field
106 * itself is counted as all blanks. */
107 if ( i < RT_UOFFSETOF(RTTARRECORD, h.chksum)
108 || i >= RT_UOFFSETOF(RTTARRECORD, h.linkflag))
109 check += pRecord->d[i];
110 else
111 check += ' ';
112 /* Additional check if all fields are zero, which indicate EOF. */
113 zero += pRecord->d[i];
114 }
115
116 /* EOF? */
117 if (!zero)
118 return VERR_EOF;
119
120 *pChkSum = check;
121 return VINF_SUCCESS;
122}
123
124static int rtTarCheckHeader(PRTTARRECORD pRecord)
125{
126 uint32_t check;
127 int rc = rtTarCalcChkSum(pRecord, &check);
128 /* EOF? */
129 if (RT_FAILURE(rc))
130 return rc;
131
132 /* Verify the checksum */
133 uint32_t sum;
134 rc = RTStrToUInt32Full(pRecord->h.chksum, 8, &sum);
135 if (RT_SUCCESS(rc) && sum == check)
136 return VINF_SUCCESS;
137 return VERR_TAR_CHKSUM_MISMATCH;
138}
139
140static int rtTarCopyFileFrom(RTFILE hFile, const char *pszTargetName, PRTTARRECORD pRecord)
141{
142 RTFILE hNewFile;
143 /* Open the target file */
144 int rc = RTFileOpen(&hNewFile, pszTargetName, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
145 if (RT_FAILURE(rc))
146 return rc;
147
148/**@todo r=bird: Use a bigger buffer here, see comment in rtTarCopyFileTo. */
149
150 uint64_t cbToCopy = RTStrToUInt64(pRecord->h.size);
151 size_t cbAllWritten = 0;
152 RTTARRECORD record;
153 /* Copy the content from hFile over to pszTargetName. This is done block
154 * wise in 512 byte steps. After this copying is finished hFile will be on
155 * a 512 byte boundary, regardless if the file copied is 512 byte size
156 * aligned. */
157 for (;;)
158 {
159 /* Finished already? */
160 if (cbAllWritten == cbToCopy)
161 break;
162 /* Read one block */
163 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
164 if (RT_FAILURE(rc))
165 break;
166 size_t cbToWrite = sizeof(record);
167 /* Check for the last block which has not to be 512 bytes in size. */
168 if (cbAllWritten + cbToWrite > cbToCopy)
169 cbToWrite = cbToCopy - cbAllWritten;
170 /* Write the block */
171 rc = RTFileWrite(hNewFile, &record, cbToWrite, NULL);
172 if (RT_FAILURE(rc))
173 break;
174 /* Count how many bytes are written already */
175 cbAllWritten += cbToWrite;
176 }
177
178 /* Now set all file attributes */
179 if (RT_SUCCESS(rc))
180 {
181 int32_t mode;
182 rc = RTStrToInt32Full(pRecord->h.mode, 8, &mode);
183 if (RT_SUCCESS(rc))
184 {
185 mode |= RTFS_TYPE_FILE; /* For now we support regular files only */
186 /* Set the mode */
187 rc = RTFileSetMode(hNewFile, mode);
188 }
189 }
190 /* Make sure the called doesn't mix truncated tar files with the official
191 * end indicated by rtTarCalcChkSum. */
192 else if (rc == VERR_EOF)
193 rc = VERR_FILE_IO_ERROR;
194
195 RTFileClose(hNewFile);
196
197 /* Delete the freshly created file in the case of an error */
198 if (RT_FAILURE(rc))
199 RTFileDelete(pszTargetName);
200
201 return rc;
202}
203
204static int rtTarCopyFileTo(RTFILE hFile, const char *pszSrcName)
205{
206 RTFILE hOldFile;
207 /* Open the source file */
208 int rc = RTFileOpen(&hOldFile, pszSrcName, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
209 if (RT_FAILURE(rc))
210 return rc;
211
212 /* Get the size of the source file */
213 uint64_t cbSize;
214 rc = RTFileGetSize(hOldFile, &cbSize);
215 if (RT_FAILURE(rc))
216 {
217 RTFileClose(hOldFile);
218 return rc;
219 }
220 /* Get some info from the source file */
221 RTFSOBJINFO info;
222 RTUID uid = 0;
223 RTGID gid = 0;
224 RTFMODE fmode = 0600; /* Make some save default */
225 int64_t mtime = 0;
226 /* This isn't critical. Use the defaults if it fails. */
227 rc = RTFileQueryInfo(hOldFile, &info, RTFSOBJATTRADD_UNIX);
228 if (RT_SUCCESS(rc))
229 {
230 fmode = info.Attr.fMode & RTFS_UNIX_MASK;
231 uid = info.Attr.u.Unix.uid;
232 gid = info.Attr.u.Unix.gid;
233 mtime = RTTimeSpecGetSeconds(&info.ModificationTime);
234 }
235
236 /* Fill the header record */
237 RTTARRECORD record;
238 RT_ZERO(record);
239 RTStrPrintf(record.h.name, sizeof(record.h.name), "%s", RTPathFilename(pszSrcName));
240 RTStrPrintf(record.h.mode, sizeof(record.h.mode), "%0.7o", fmode);
241 RTStrPrintf(record.h.uid, sizeof(record.h.uid), "%0.7o", uid);
242 RTStrPrintf(record.h.gid, sizeof(record.h.gid), "%0.7o", gid);
243 RTStrPrintf(record.h.size, sizeof(record.h.size), "%0.11o", cbSize);
244 RTStrPrintf(record.h.mtime, sizeof(record.h.mtime), "%0.11o", mtime);
245 RTStrPrintf(record.h.magic, sizeof(record.h.magic), "ustar ");
246 RTStrPrintf(record.h.uname, sizeof(record.h.uname), "someone");
247 RTStrPrintf(record.h.gname, sizeof(record.h.gname), "someone");
248 record.h.linkflag = LF_NORMAL;
249
250 /* Create the checksum out of the new header */
251 uint32_t chksum;
252 rc = rtTarCalcChkSum(&record, &chksum);
253 if (RT_SUCCESS(rc))
254 {
255 RTStrPrintf(record.h.chksum, sizeof(record.h.chksum), "%0.7o", chksum);
256
257 /* Write the header first */
258 rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
259 if (RT_SUCCESS(rc))
260 {
261/** @todo r=bird: using a 64KB buffer here instead of 0.5KB would probably be
262 * a good thing. */
263 uint64_t cbAllWritten = 0;
264 /* Copy the content from pszSrcName over to hFile. This is done block
265 * wise in 512 byte steps. After this copying is finished hFile will be
266 * on a 512 byte boundary, regardless if the file copied is 512 byte
267 * size aligned. */
268 for (;;)
269 {
270 if (cbAllWritten >= cbSize)
271 break;
272 size_t cbToRead = sizeof(record);
273 /* Last record? */
274 if (cbAllWritten + cbToRead > cbSize)
275 {
276 /* Initialize with zeros */
277 RT_ZERO(record);
278 cbToRead = cbSize - cbAllWritten;
279 }
280 /* Read one block */
281 rc = RTFileRead(hOldFile, &record, cbToRead, NULL);
282 if (RT_FAILURE(rc))
283 break;
284 /* Write one block */
285 rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
286 if (RT_FAILURE(rc))
287 break;
288 /* Count how many bytes are written already */
289 cbAllWritten += sizeof(record);
290 }
291
292 /* Make sure the called doesn't mix truncated tar files with the
293 * official end indicated by rtTarCalcChkSum. */
294 if (rc == VERR_EOF)
295 rc = VERR_FILE_IO_ERROR;
296 }
297 }
298
299 RTFileClose(hOldFile);
300 return rc;
301}
302
303static int rtTarSkipData(RTFILE hFile, PRTTARRECORD pRecord)
304{
305 int rc = VINF_SUCCESS;
306 /* Seek over the data parts (512 bytes aligned) */
307 int64_t offSeek = RT_ALIGN(RTStrToInt64(pRecord->h.size), sizeof(RTTARRECORD));
308 if (offSeek > 0)
309 rc = RTFileSeek(hFile, offSeek, RTFILE_SEEK_CURRENT, NULL);
310 return rc;
311}
312
313
314RTR3DECL(int) RTTarQueryFileExists(const char *pszTarFile, const char *pszFile)
315{
316 /* Validate input */
317 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
318 AssertPtrReturn(pszFile, VERR_INVALID_POINTER);
319
320 /* Open the tar file */
321 RTFILE hFile;
322 int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
323 if (RT_FAILURE(rc))
324 return rc;
325
326 bool fFound = false;
327 RTTARRECORD record;
328 for (;;)
329 {
330/** @todo r=bird: the reading, validation and EOF check done here should be
331 * moved to a separate helper function. That would make it easiser to
332 * distinguish genuine-end-of-tar-file and VERR_EOF caused by a
333 * trunacted file. That said, rtTarSkipData won't return VERR_EOF, at
334 * least not on unix, since it's not a sin to seek beyond the end of a
335 * file. */
336 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
337 /* Check for error or EOF. */
338 if (RT_FAILURE(rc))
339 break;
340 /* Check for EOF & data integrity */
341 rc = rtTarCheckHeader(&record);
342 if (RT_FAILURE(rc))
343 break;
344 /* We support normal files only */
345 if ( record.h.linkflag == LF_OLDNORMAL
346 || record.h.linkflag == LF_NORMAL)
347 {
348 if (!RTStrCmp(record.h.name, pszFile))
349 {
350 fFound = true;
351 break;
352 }
353 }
354 rc = rtTarSkipData(hFile, &record);
355 if (RT_FAILURE(rc))
356 break;
357 }
358
359 RTFileClose(hFile);
360
361 if (rc == VERR_EOF)
362 rc = VINF_SUCCESS;
363
364 /* Something found? */
365 if ( RT_SUCCESS(rc)
366 && !fFound)
367 rc = VERR_FILE_NOT_FOUND;
368
369 return rc;
370}
371
372RTR3DECL(int) RTTarList(const char *pszTarFile, char ***ppapszFiles, size_t *pcFiles)
373{
374 /* Validate input */
375 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
376 AssertPtrReturn(ppapszFiles, VERR_INVALID_POINTER);
377 AssertPtrReturn(pcFiles, VERR_INVALID_POINTER);
378
379 /* Open the tar file */
380 RTFILE hFile;
381 int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
382 if (RT_FAILURE(rc))
383 return rc;
384
385 /* Initialize the file name array with one slot */
386 size_t cFilesAlloc = 1;
387 char **papszFiles = (char**)RTMemAlloc(sizeof(char *));
388 if (!papszFiles)
389 {
390 RTFileClose(hFile);
391 return VERR_NO_MEMORY;
392 }
393
394 /* Iterate through the tar file record by record. Skip data records as we
395 * didn't need them. */
396 RTTARRECORD record;
397 size_t cFiles = 0;
398 for (;;)
399 {
400 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
401 /* Check for error or EOF. */
402 if (RT_FAILURE(rc))
403 break;
404 /* Check for EOF & data integrity */
405 rc = rtTarCheckHeader(&record);
406 if (RT_FAILURE(rc))
407 break;
408 /* We support normal files only */
409 if ( record.h.linkflag == LF_OLDNORMAL
410 || record.h.linkflag == LF_NORMAL)
411 {
412 if (cFiles >= cFilesAlloc)
413 {
414 /* Double the array size, make sure the size doesn't wrap. */
415 void *pvNew = NULL;
416 size_t cbNew = cFilesAlloc * sizeof(char *) * 2;
417 if (cbNew / sizeof(char *) / 2 == cFilesAlloc)
418 pvNew = RTMemRealloc(papszFiles, cbNew);
419 if (!pvNew)
420 {
421 rc = VERR_NO_MEMORY;
422 break;
423 }
424 papszFiles = (char **)pvNew;
425 cFilesAlloc *= 2;
426 }
427
428 /* Duplicate the name */
429 papszFiles[cFiles] = RTStrDup(record.h.name);
430 if (!papszFiles[cFiles])
431 {
432 rc = VERR_NO_MEMORY;
433 break;
434 }
435 cFiles++;
436 }
437 rc = rtTarSkipData(hFile, &record);
438 if (RT_FAILURE(rc))
439 break;
440 }
441
442 RTFileClose(hFile);
443
444 if (rc == VERR_EOF)
445 rc = VINF_SUCCESS;
446
447 /* Return the file array on success, dispose of it on failure. */
448 if (RT_SUCCESS(rc))
449 {
450 *pcFiles = cFiles;
451 *ppapszFiles = papszFiles;
452 }
453 else
454 {
455 while (cFiles-- > 0)
456 RTStrFree(papszFiles[cFiles]);
457 RTMemFree(papszFiles);
458 }
459 return rc;
460}
461
462RTR3DECL(int) RTTarExtractFiles(const char *pszTarFile, const char *pszOutputDir, const char * const *papszFiles, size_t cFiles)
463{
464 /* Validate input */
465 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
466 AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
467 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
468
469 /* Open the tar file */
470 RTFILE hFile;
471 int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
472 if (RT_FAILURE(rc))
473 return rc;
474
475 /* Iterate through the tar file record by record. */
476 RTTARRECORD record;
477 char **paExtracted = (char **)RTMemTmpAllocZ(sizeof(char *) * cFiles);
478 if (paExtracted)
479 {
480 size_t cExtracted = 0;
481 for (;;)
482 {
483 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
484 /* Check for error or EOF. */
485 if (RT_FAILURE(rc))
486 break;
487 /* Check for EOF & data integrity */
488 rc = rtTarCheckHeader(&record);
489 if (RT_FAILURE(rc))
490 break;
491 /* We support normal files only */
492 if ( record.h.linkflag == LF_OLDNORMAL
493 || record.h.linkflag == LF_NORMAL)
494 {
495 bool fFound = false;
496 for (size_t i = 0; i < cFiles; ++i)
497 {
498 if (!RTStrCmp(record.h.name, papszFiles[i]))
499 {
500 fFound = true;
501 if (cExtracted < cFiles)
502 {
503 char *pszTargetFile;
504 rc = RTStrAPrintf(&pszTargetFile, "%s/%s", pszOutputDir, papszFiles[i]);
505 if (rc > 0)
506 {
507 rc = rtTarCopyFileFrom(hFile, pszTargetFile, &record);
508 if (RT_SUCCESS(rc))
509 paExtracted[cExtracted++] = pszTargetFile;
510 else
511 RTStrFree(pszTargetFile);
512 }
513 else
514 rc = VERR_NO_MEMORY;
515 }
516 else
517 rc = VERR_ALREADY_EXISTS;
518 break;
519 }
520 }
521 if (RT_FAILURE(rc))
522 break;
523 /* If the current record isn't a file in the file list we have to
524 * skip the data */
525 if (!fFound)
526 {
527 rc = rtTarSkipData(hFile, &record);
528 if (RT_FAILURE(rc))
529 break;
530 }
531 }
532 }
533
534 if (rc == VERR_EOF)
535 rc = VINF_SUCCESS;
536
537 /* If we didn't found all files, indicate an error */
538 if (cExtracted != cFiles && RT_SUCCESS(rc))
539 rc = VERR_FILE_NOT_FOUND;
540
541 /* Cleanup the names of the extracted files, deleting them on failure. */
542 while (cExtracted-- > 0)
543 {
544 if (RT_FAILURE(rc))
545 RTFileDelete(paExtracted[cExtracted]);
546 RTStrFree(paExtracted[cExtracted]);
547 }
548 RTMemTmpFree(paExtracted);
549 }
550 else
551 rc = VERR_NO_TMP_MEMORY;
552
553 RTFileClose(hFile);
554 return rc;
555}
556
557RTR3DECL(int) RTTarExtractByIndex(const char *pszTarFile, const char *pszOutputDir, size_t iIndex, char **ppszFileName)
558{
559 /* Validate input */
560 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
561 AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
562
563 /* Open the tar file */
564 RTFILE hFile;
565 int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
566 if (RT_FAILURE(rc))
567 return rc;
568
569 /* Iterate through the tar file record by record. */
570 RTTARRECORD record;
571 size_t iFile = 0;
572 bool fFound = false;
573 for (;;)
574 {
575 rc = RTFileRead(hFile, &record, sizeof(record), NULL);
576 /* Check for error or EOF. */
577 if (RT_FAILURE(rc))
578 break;
579 /* Check for EOF & data integrity */
580 rc = rtTarCheckHeader(&record);
581 if (RT_FAILURE(rc))
582 break;
583 /* We support normal files only */
584 if ( record.h.linkflag == LF_OLDNORMAL
585 || record.h.linkflag == LF_NORMAL)
586 {
587 if (iIndex == iFile)
588 {
589 fFound = true;
590 char *pszTargetName;
591 rc = RTStrAPrintf(&pszTargetName, "%s/%s", pszOutputDir, record.h.name);
592 if (rc > 0)
593 {
594 rc = rtTarCopyFileFrom(hFile, pszTargetName, &record);
595 /* On success pass on the filename if requested. */
596 if ( RT_SUCCESS(rc)
597 && ppszFileName)
598 *ppszFileName = pszTargetName;
599 else
600 RTStrFree(pszTargetName);
601 }
602 else
603 rc = VERR_NO_MEMORY;
604 break;
605 }
606 }
607 rc = rtTarSkipData(hFile, &record);
608 if (RT_FAILURE(rc))
609 break;
610 ++iFile;
611 }
612
613 RTFileClose(hFile);
614
615 if (rc == VERR_EOF)
616 rc = VINF_SUCCESS;
617
618 /* If we didn't found the index, indicate an error */
619 if (!fFound && RT_SUCCESS(rc))
620 rc = VERR_FILE_NOT_FOUND;
621
622 return rc;
623}
624
625RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles)
626{
627 /* Validate input */
628 AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
629 AssertPtrReturn(papszFiles, VERR_INVALID_POINTER);
630
631 /* Open the tar file */
632 RTFILE hFile;
633 int rc = RTFileOpen(&hFile, pszTarFile, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
634 if (RT_FAILURE(rc))
635 return rc;
636
637 for (size_t i = 0; i < cFiles; ++i)
638 {
639 rc = rtTarCopyFileTo(hFile, papszFiles[i]);
640 if (RT_FAILURE(rc))
641 break;
642 }
643
644 /* gtar gives a warning, but the documentation says EOF is indicated by a
645 * zero block. Disabled for now. */
646#if 0
647 if (RT_SUCCESS(rc))
648 {
649 /* Append the EOF record which is filled all by zeros */
650 RTTARRECORD record;
651 ASMMemFill32(&record, sizeof(record), 0);
652 rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
653 }
654#endif
655
656 /* Time to close the new tar archive */
657 RTFileClose(hFile);
658
659 /* Delete the freshly created tar archive on failure */
660 if (RT_FAILURE(rc))
661 RTFileDelete(pszTarFile);
662
663 return rc;
664}
665
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette