2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afs/param.h>
31 #define MAXINSIZE 1300 /* pioctl complains if data is larger than this */
33 static char space[MAXSIZE];
34 static char tspace[1024];
37 static struct ubik_client *uclient;
38 #endif /* not WIN32 */
41 static char pn[] = "symlink";
42 static int rxInitDone = 0;
53 if (t >= 'A' && t <= 'Z') t += 0x20;
54 if (u >= 'A' && u <= 'Z') u += 0x20;
60 /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */
61 static int InAFS(register char *apath)
63 struct ViceIoctl blob;
64 register afs_int32 code;
67 blob.out_size = MAXSIZE;
70 code = pioctl(apath, VIOC_FILE_CELL_NAME, &blob, 1);
72 if ((errno == EINVAL) || (errno == ENOENT))
79 IsFreelanceRoot(char *apath)
81 struct ViceIoctl blob;
85 blob.out_size = MAXSIZE;
88 code = pioctl(apath, VIOC_FILE_CELL_NAME, &blob, 1);
90 return !strcmp("Freelance.Local.Root",space);
91 return 1; /* assume it is because it is more restrictive that way */
94 #define AFSCLIENT_ADMIN_GROUPNAME "AFS Client Admins"
96 static BOOL IsAdmin (void)
98 static BOOL fAdmin = FALSE;
99 static BOOL fTested = FALSE;
103 /* Obtain the SID for the AFS client admin group. If the group does
104 * not exist, then assume we have AFS client admin privileges.
106 PSID psidAdmin = NULL;
107 DWORD dwSize, dwSize2;
108 char pszAdminGroup[ MAX_COMPUTERNAME_LENGTH + sizeof(AFSCLIENT_ADMIN_GROUPNAME) + 2 ];
109 char *pszRefDomain = NULL;
110 SID_NAME_USE snu = SidTypeGroup;
112 dwSize = sizeof(pszAdminGroup);
114 if (!GetComputerName(pszAdminGroup, &dwSize)) {
115 /* Can't get computer name. We return false in this case.
116 Retain fAdmin and fTested. This shouldn't happen.*/
123 strcat(pszAdminGroup,"\\");
124 strcat(pszAdminGroup, AFSCLIENT_ADMIN_GROUPNAME);
126 LookupAccountName(NULL, pszAdminGroup, NULL, &dwSize, NULL, &dwSize2, &snu);
127 /* that should always fail. */
129 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
130 /* if we can't find the group, then we allow the operation */
135 if (dwSize == 0 || dwSize2 == 0) {
141 psidAdmin = (PSID)malloc(dwSize); memset(psidAdmin,0,dwSize);
143 pszRefDomain = (char *)malloc(dwSize2);
144 assert(pszRefDomain);
146 if (!LookupAccountName(NULL, pszAdminGroup, psidAdmin, &dwSize, pszRefDomain, &dwSize2, &snu)) {
147 /* We can't lookup the group now even though we looked it up earlier.
148 Could this happen? */
151 /* Then open our current ProcessToken */
154 if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken))
157 if (!CheckTokenMembership(hToken, psidAdmin, &fAdmin)) {
158 /* We'll have to allocate a chunk of memory to store the list of
159 * groups to which this user belongs; find out how much memory
163 PTOKEN_GROUPS pGroups;
165 GetTokenInformation (hToken, TokenGroups, NULL, dwSize, &dwSize);
167 pGroups = (PTOKEN_GROUPS)malloc(dwSize);
170 /* Allocate that buffer, and read in the list of groups. */
171 if (GetTokenInformation (hToken, TokenGroups, pGroups, dwSize, &dwSize))
173 /* Look through the list of group SIDs and see if any of them
174 * matches the AFS Client Admin group SID.
177 for (; (!fAdmin) && (iGroup < pGroups->GroupCount); ++iGroup)
179 if (EqualSid (psidAdmin, pGroups->Groups[ iGroup ].Sid)) {
189 /* if do not have permission because we were not explicitly listed
190 * in the Admin Client Group let's see if we are the SYSTEM account
193 PTOKEN_USER pTokenUser;
194 SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
195 PSID pSidLocalSystem = 0;
198 GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
200 pTokenUser = (PTOKEN_USER)malloc(dwSize);
203 if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
204 gle = GetLastError();
206 if (AllocateAndInitializeSid( &SIDAuth, 1,
207 SECURITY_LOCAL_SYSTEM_RID,
211 if (EqualSid(pTokenUser->User.Sid, pSidLocalSystem)) {
215 FreeSid(pSidLocalSystem);
233 /* return a static pointer to a buffer */
234 static char *Parent(apath)
237 strcpy(tspace, apath);
238 tp = strrchr(tspace, '\\');
240 *(tp+1) = 0; /* lv trailing slash so Parent("k:\foo") is "k:\" not "k:" */
243 fs_ExtractDriveLetter(apath, tspace);
250 static ListLinkCmd(as)
251 register struct cmd_syndesc *as; {
252 register afs_int32 code;
253 struct ViceIoctl blob;
255 register struct cmd_item *ti;
256 char orig_name[1024]; /*Original name, may be modified*/
257 char true_name[1024]; /*``True'' dirname (e.g., symlink target)*/
258 char parent_dir[1024]; /*Parent directory of true name*/
259 register char *last_component; /*Last component of true name*/
261 struct stat statbuff; /*Buffer for status info*/
262 #endif /* not WIN32 */
264 int link_chars_read; /*Num chars read in readlink()*/
265 #endif /* not WIN32 */
266 int thru_symlink; /*Did we get to a mount point via a symlink?*/
269 for(ti=as->parms[0].items; ti; ti=ti->next) {
273 strcpy(orig_name, ti->data);
274 #else /* not WIN32 */
275 sprintf(orig_name, "%s%s",
276 (ti->data[0] == '/') ? "" : "./",
278 #endif /* not WIN32 */
281 if (lstat(orig_name, &statbuff) < 0) {
282 /* if lstat fails, we should still try the pioctl, since it
283 may work (for example, lstat will fail, but pioctl will
284 work if the volume of offline (returning ENODEV). */
285 statbuff.st_mode = S_IFDIR; /* lie like pros */
289 * The lstat succeeded. If the given file is a symlink, substitute
290 * the file name with the link name.
292 if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
295 * Read name of resolved file.
297 link_chars_read = readlink(orig_name, true_name, 1024);
298 if (link_chars_read <= 0) {
299 fprintf(stderr,"%s: Can't read target name for '%s' symbolic link!\n",
305 * Add a trailing null to what was read, bump the length.
307 true_name[link_chars_read++] = 0;
310 * If the symlink is an absolute pathname, we're fine. Otherwise, we
311 * have to create a full pathname using the original name and the
312 * relative symlink name. Find the rightmost slash in the original
313 * name (we know there is one) and splice in the symlink value.
315 if (true_name[0] != '\\') {
316 last_component = (char *) strrchr(orig_name, '\\');
317 strcpy(++last_component, true_name);
318 strcpy(true_name, orig_name);
322 strcpy(true_name, orig_name);
324 strcpy(true_name, orig_name);
328 * Find rightmost slash, if any.
330 last_component = (char *) strrchr(true_name, '\\');
332 last_component = (char *) strrchr(true_name, '/');
333 if (last_component) {
335 * Found it. Designate everything before it as the parent directory,
336 * everything after it as the final component.
338 strncpy(parent_dir, true_name, last_component - true_name + 1);
339 parent_dir[last_component - true_name + 1] = 0;
340 last_component++; /*Skip the slash*/
344 * No slash appears in the given file name. Set parent_dir to the current
345 * directory, and the last component as the given name.
347 fs_ExtractDriveLetter(true_name, parent_dir);
348 strcat(parent_dir, ".");
349 last_component = true_name;
350 fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
353 if (strcmp(last_component, ".") == 0 || strcmp(last_component, "..") == 0) {
354 fprintf(stderr,"%s: you may not use '.' or '..' as the last component\n", pn);
355 fprintf(stderr,"%s: of a name in the 'symlink list' command.\n", pn);
359 blob.in = last_component;
360 blob.in_size = strlen(last_component)+1;
361 blob.out_size = MAXSIZE;
363 memset(space, 0, MAXSIZE);
365 code = pioctl(parent_dir, VIOC_LISTSYMLINK, &blob, 1);
368 printf("'%s' is a %ssymlink to '%s'\n",
370 (thru_symlink ? "symbolic link, leading to a " : ""),
376 fprintf(stderr,"'%s' is not a symlink.\n",
379 Die(errno, (ti->data ? ti->data : parent_dir));
386 static MakeLinkCmd(as)
387 register struct cmd_syndesc *as; {
388 register afs_int32 code;
389 struct ViceIoctl blob;
392 parent = Parent(as->parms[0].items->data);
394 if (!InAFS(parent)) {
395 fprintf(stderr,"%s: symlinks must be created within the AFS file system\n", pn);
399 if ( IsFreelanceRoot(parent) && !IsAdmin() ) {
400 fprintf(stderr,"%s: Only AFS Client Administrators may alter the root.afs volume\n", pn);
404 strcpy(space, as->parms[1].items->data);
406 /* create symlink with a special pioctl for Windows NT, since it doesn't
407 * have a symlink system call.
410 /* TODO: Code needs to go here to prevent the creation of symlinks
411 * in \\AFS\all when not in the "AFS Client Admins" group.
415 blob.in_size = 1 + strlen(space);
418 code = pioctl(as->parms[0].items->data, VIOC_SYMLINK, &blob, 0);
419 #else /* not WIN32 */
420 code = symlink(space, as->parms[0].items->data);
421 #endif /* not WIN32 */
423 Die(errno, as->parms[0].items->data);
430 * Delete AFS symlinks. Variables are used as follows:
431 * tbuffer: Set to point to the null-terminated directory name of the
432 * symlink (or ``.'' if none is provided)
433 * tp: Set to point to the actual name of the symlink to nuke.
435 static RemoveLinkCmd(as)
436 register struct cmd_syndesc *as; {
437 register afs_int32 code=0;
438 struct ViceIoctl blob;
439 register struct cmd_item *ti;
444 for(ti=as->parms[0].items; ti; ti=ti->next) {
446 tp = (char *) strrchr(ti->data, '\\');
448 tp = (char *) strrchr(ti->data, '/');
450 strncpy(tbuffer, ti->data, code=tp-ti->data+1); /* the dir name */
452 tp++; /* skip the slash */
455 fs_ExtractDriveLetter(ti->data, tbuffer);
456 strcat(tbuffer, ".");
458 fs_StripDriveLetter(tp, tp, 0);
461 blob.in_size = strlen(tp)+1;
463 blob.out_size = sizeof(lsbuffer);
464 code = pioctl(tbuffer, VIOC_LISTSYMLINK, &blob, 0);
467 fprintf(stderr,"symlink: '%s' is not a symlink.\n", ti->data);
469 Die(errno, ti->data);
471 continue; /* don't bother trying */
474 if ( IsFreelanceRoot(Parent(ti->data)) && !IsAdmin() ) {
475 fprintf(stderr,"symlink: Only AFS Client Administrators may alter the root.afs volume\n");
482 blob.in_size = strlen(tp)+1;
483 code = pioctl(tbuffer, VIOC_DELSYMLINK, &blob, 0);
485 Die(errno, ti->data);
491 static struct ViceIoctl gblob;
492 static int debug = 0;
497 register afs_int32 code;
498 register struct cmd_syndesc *ts;
502 * The following signal action for AIX is necessary so that in case of a
503 * crash (i.e. core is generated) we can include the user's data section
504 * in the core dump. Unfortunately, by default, only a partial core is
505 * generated which, in many cases, isn't too useful.
507 struct sigaction nsa;
509 sigemptyset(&nsa.sa_mask);
510 nsa.sa_handler = SIG_DFL;
511 nsa.sa_flags = SA_FULLDUMP;
512 sigaction(SIGSEGV, &nsa, NULL);
517 WSAStartup(0x0101, &WSAjunk);
520 /* try to find volume location information */
525 ts = cmd_CreateSyntax("list", ListLinkCmd, 0, "list symlink");
526 cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
528 ts = cmd_CreateSyntax("make", MakeLinkCmd, 0, "make symlink");
529 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name");
530 cmd_AddParm(ts, "-to", CMD_SINGLE, 0, "target");
532 ts = cmd_CreateSyntax("rm", RemoveLinkCmd, 0, "remove symlink");
533 cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
535 code = cmd_Dispatch(argc, argv);
538 if (rxInitDone) rx_Finalize();
539 #endif /* not WIN32 */
544 void Die(code, filename)
549 if (code == EINVAL) {
551 fprintf(stderr,"%s: Invalid argument; it is possible that %s is not in AFS.\n", pn, filename);
552 else fprintf(stderr,"%s: Invalid argument.\n", pn);
554 else if (code == ENOENT) {
555 if (filename) fprintf(stderr,"%s: File '%s' doesn't exist\n", pn, filename);
556 else fprintf(stderr,"%s: no such file returned\n", pn);
558 else if (code == EROFS) fprintf(stderr,"%s: You can not change a backup or readonly volume\n", pn);
559 else if (code == EACCES || code == EPERM) {
560 if (filename) fprintf(stderr,"%s: You don't have the required access rights on '%s'\n", pn, filename);
561 else fprintf(stderr,"%s: You do not have the required rights to do this operation\n", pn);
563 else if (code == ENODEV) {
564 fprintf(stderr,"%s: AFS service may not have started.\n", pn);
566 else if (code == ESRCH) {
567 fprintf(stderr,"%s: Cell name not recognized.\n", pn);
569 else if (code == EPIPE) {
570 fprintf(stderr,"%s: Volume name or ID not recognized.\n", pn);
572 else if (code == EFBIG) {
573 fprintf(stderr,"%s: Cache size too large.\n", pn);
575 else if (code == ETIMEDOUT) {
577 fprintf(stderr,"%s:'%s': Connection timed out", pn, filename);
579 fprintf(stderr,"%s: Connection timed out", pn);
582 if (filename) fprintf(stderr,"%s:'%s'", pn, filename);
583 else fprintf(stderr,"%s", pn);
585 fprintf(stderr, ": code 0x%x\n", code);
586 #else /* not WIN32 */
587 fprintf(stderr,": %s\n", error_message(code));
588 #endif /* not WIN32 */