windows-remove-give-up-callbacks-scache-recycling-20070627
[openafs.git] / src / WINNT / afsd / symlink.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
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
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <stdlib.h>
15 #include <malloc.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include <time.h>
19 #include <winsock2.h>
20 #include <errno.h>
21 #include <assert.h>
22
23 #include <osi.h>
24 #include <afsint.h>
25 #include <WINNT\afsreg.h>
26
27 #include "fs_utils.h"
28 #include "cmd.h"
29
30 #define MAXNAME 100
31 #define MAXSIZE 2048
32 #define MAXINSIZE 1300    /* pioctl complains if data is larger than this */
33
34 static char space[MAXSIZE];
35 static char tspace[1024];
36
37 #ifndef WIN32
38 static struct ubik_client *uclient;
39 #endif /* not WIN32 */
40
41
42 static char pn[] = "symlink";
43 static int rxInitDone = 0;
44
45 void Die();
46
47 foldcmp (a, b)
48     register char *a;
49     register char *b; {
50     register char t, u;
51     while (1) {
52         t = *a++;
53         u = *b++;
54         if (t >= 'A' && t <= 'Z') t += 0x20;
55         if (u >= 'A' && u <= 'Z') u += 0x20;
56         if (t != u) return 1;
57         if (t == 0) return 0;
58     }
59 }
60
61 /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */
62 static int InAFS(register char *apath)
63 {
64     struct ViceIoctl blob;
65     register afs_int32 code;
66
67     blob.in_size = 0;
68     blob.out_size = MAXSIZE;
69     blob.out = space;
70
71     code = pioctl(apath, VIOC_FILE_CELL_NAME, &blob, 1);
72     if (code) {
73         if ((errno == EINVAL) || (errno == ENOENT)) 
74             return 0;
75     }
76     return 1;
77 }
78
79 static int 
80 IsFreelanceRoot(char *apath)
81 {
82     struct ViceIoctl blob;
83     afs_int32 code;
84
85     blob.in_size = 0;
86     blob.out_size = MAXSIZE;
87     blob.out = space;
88
89     code = pioctl(apath, VIOC_FILE_CELL_NAME, &blob, 1);
90     if (code == 0)
91         return !strcmp("Freelance.Local.Root",space);
92     return 1;   /* assume it is because it is more restrictive that way */
93 }
94
95 static const char * NetbiosName(void)
96 {
97     static char buffer[1024] = "AFS";
98     HKEY  parmKey;
99     DWORD code;
100     DWORD dummyLen;
101     DWORD enabled = 0;
102
103     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
104                          0, KEY_QUERY_VALUE, &parmKey);
105     if (code == ERROR_SUCCESS) {
106         dummyLen = sizeof(buffer);
107         code = RegQueryValueEx(parmKey, "NetbiosName", NULL, NULL,
108                                buffer, &dummyLen);
109         RegCloseKey (parmKey);
110     } else {
111         strcpy(buffer, "AFS");
112     }
113     return buffer;
114 }
115
116 #define AFSCLIENT_ADMIN_GROUPNAME "AFS Client Admins"
117
118 static BOOL IsAdmin (void)
119 {
120     static BOOL fAdmin = FALSE;
121     static BOOL fTested = FALSE;
122
123     if (!fTested)
124     {
125         /* Obtain the SID for the AFS client admin group.  If the group does
126          * not exist, then assume we have AFS client admin privileges.
127          */
128         PSID psidAdmin = NULL;
129         DWORD dwSize, dwSize2;
130         char pszAdminGroup[ MAX_COMPUTERNAME_LENGTH + sizeof(AFSCLIENT_ADMIN_GROUPNAME) + 2 ];
131         char *pszRefDomain = NULL;
132         SID_NAME_USE snu = SidTypeGroup;
133
134         dwSize = sizeof(pszAdminGroup);
135
136         if (!GetComputerName(pszAdminGroup, &dwSize)) {
137             /* Can't get computer name.  We return false in this case.
138                Retain fAdmin and fTested. This shouldn't happen.*/
139             return FALSE;
140         }
141
142         dwSize = 0;
143         dwSize2 = 0;
144
145         strcat(pszAdminGroup,"\\");
146         strcat(pszAdminGroup, AFSCLIENT_ADMIN_GROUPNAME);
147
148         LookupAccountName(NULL, pszAdminGroup, NULL, &dwSize, NULL, &dwSize2, &snu);
149         /* that should always fail. */
150
151         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
152             /* if we can't find the group, then we allow the operation */
153             fAdmin = TRUE;
154             return TRUE;
155         }
156
157         if (dwSize == 0 || dwSize2 == 0) {
158             /* Paranoia */
159             fAdmin = TRUE;
160             return TRUE;
161         }
162
163         psidAdmin = (PSID)malloc(dwSize); memset(psidAdmin,0,dwSize);
164         assert(psidAdmin);
165         pszRefDomain = (char *)malloc(dwSize2);
166         assert(pszRefDomain);
167
168         if (!LookupAccountName(NULL, pszAdminGroup, psidAdmin, &dwSize, pszRefDomain, &dwSize2, &snu)) {
169             /* We can't lookup the group now even though we looked it up earlier.  
170                Could this happen? */
171             fAdmin = TRUE;
172         } else {
173             /* Then open our current ProcessToken */
174             HANDLE hToken;
175
176             if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken))
177             {
178
179                 if (!CheckTokenMembership(hToken, psidAdmin, &fAdmin)) {
180                     /* We'll have to allocate a chunk of memory to store the list of
181                      * groups to which this user belongs; find out how much memory
182                      * we'll need.
183                      */
184                     DWORD dwSize = 0;
185                     PTOKEN_GROUPS pGroups;
186
187                     GetTokenInformation (hToken, TokenGroups, NULL, dwSize, &dwSize);
188
189                     pGroups = (PTOKEN_GROUPS)malloc(dwSize);
190                     assert(pGroups);
191
192                     /* Allocate that buffer, and read in the list of groups. */
193                     if (GetTokenInformation (hToken, TokenGroups, pGroups, dwSize, &dwSize))
194                     {
195                         /* Look through the list of group SIDs and see if any of them
196                          * matches the AFS Client Admin group SID.
197                          */
198                         size_t iGroup = 0;
199                         for (; (!fAdmin) && (iGroup < pGroups->GroupCount); ++iGroup)
200                         {
201                             if (EqualSid (psidAdmin, pGroups->Groups[ iGroup ].Sid)) {
202                                 fAdmin = TRUE;
203                             }
204                         }
205                     }
206
207                     if (pGroups)
208                         free(pGroups);
209                 }
210
211                 /* if do not have permission because we were not explicitly listed
212                  * in the Admin Client Group let's see if we are the SYSTEM account
213                  */
214                 if (!fAdmin) {
215                     PTOKEN_USER pTokenUser;
216                     SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
217                     PSID pSidLocalSystem = 0;
218                     DWORD gle;
219
220                     GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
221
222                     pTokenUser = (PTOKEN_USER)malloc(dwSize);
223                     assert(pTokenUser);
224
225                     if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
226                         gle = GetLastError();
227
228                     if (AllocateAndInitializeSid( &SIDAuth, 1,
229                                                   SECURITY_LOCAL_SYSTEM_RID,
230                                                   0, 0, 0, 0, 0, 0, 0,
231                                                   &pSidLocalSystem))
232                     {
233                         if (EqualSid(pTokenUser->User.Sid, pSidLocalSystem)) {
234                             fAdmin = TRUE;
235                         }
236
237                         FreeSid(pSidLocalSystem);
238                     }
239
240                     if ( pTokenUser )
241                         free(pTokenUser);
242                 }
243             }
244         }
245
246         free(psidAdmin);
247         free(pszRefDomain);
248
249         fTested = TRUE;
250     }
251
252     return fAdmin;
253 }
254
255 /* return a static pointer to a buffer */
256 static char *Parent(apath)
257 char *apath; {
258     register char *tp;
259     strcpy(tspace, apath);
260     tp = strrchr(tspace, '\\');
261     if (tp) {
262         *(tp+1) = 0;    /* lv trailing slash so Parent("k:\foo") is "k:\" not "k:" */
263     }
264     else {
265         fs_ExtractDriveLetter(apath, tspace);
266         strcat(tspace, ".");
267     }
268     return tspace;
269 }
270
271
272 static ListLinkCmd(as)
273 register struct cmd_syndesc *as; {
274     register afs_int32 code;
275     struct ViceIoctl blob;
276     int error;
277     register struct cmd_item *ti;
278     char orig_name[1024];               /*Original name, may be modified*/
279     char true_name[1024];               /*``True'' dirname (e.g., symlink target)*/
280     char parent_dir[1024];              /*Parent directory of true name*/
281     register char *last_component;      /*Last component of true name*/
282 #ifndef WIN32
283     struct stat statbuff;               /*Buffer for status info*/
284 #endif /* not WIN32 */
285 #ifndef WIN32
286     int link_chars_read;                /*Num chars read in readlink()*/
287 #endif /* not WIN32 */
288     int thru_symlink;                   /*Did we get to a mount point via a symlink?*/
289     
290     error = 0;
291     for(ti=as->parms[0].items; ti; ti=ti->next) {
292         /* once per file */
293         thru_symlink = 0;
294 #ifdef WIN32
295         strcpy(orig_name, ti->data);
296 #else /* not WIN32 */
297         sprintf(orig_name, "%s%s",
298                 (ti->data[0] == '/') ? "" : "./",
299                 ti->data);
300 #endif /* not WIN32 */
301
302 #ifndef WIN32
303         if (lstat(orig_name, &statbuff) < 0) {
304             /* if lstat fails, we should still try the pioctl, since it
305                 may work (for example, lstat will fail, but pioctl will
306                     work if the volume of offline (returning ENODEV). */
307             statbuff.st_mode = S_IFDIR; /* lie like pros */
308         }
309
310         /*
311          * The lstat succeeded.  If the given file is a symlink, substitute
312          * the file name with the link name.
313          */
314         if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
315             thru_symlink = 1;
316             /*
317              * Read name of resolved file.
318              */
319             link_chars_read = readlink(orig_name, true_name, 1024);
320             if (link_chars_read <= 0) {
321                 fprintf(stderr,"%s: Can't read target name for '%s' symbolic link!\n",
322                        pn, orig_name);
323                 exit(1);
324             }
325
326             /*
327              * Add a trailing null to what was read, bump the length.
328              */
329             true_name[link_chars_read++] = 0;
330
331             /*
332              * If the symlink is an absolute pathname, we're fine.  Otherwise, we
333              * have to create a full pathname using the original name and the
334              * relative symlink name.  Find the rightmost slash in the original
335              * name (we know there is one) and splice in the symlink value.
336              */
337             if (true_name[0] != '\\') {
338                 last_component = (char *) strrchr(orig_name, '\\');
339                 strcpy(++last_component, true_name);
340                 strcpy(true_name, orig_name);
341             }
342         }
343         else
344             strcpy(true_name, orig_name);
345 #else   /* WIN32 */
346         strcpy(true_name, orig_name);
347 #endif /* WIN32 */
348
349         /*
350          * Find rightmost slash, if any.
351          */
352         last_component = (char *) strrchr(true_name, '\\');
353         if (!last_component)
354             last_component = (char *) strrchr(true_name, '/');
355         if (last_component) {
356             /*
357              * Found it.  Designate everything before it as the parent directory,
358              * everything after it as the final component.
359              */
360             strncpy(parent_dir, true_name, last_component - true_name + 1);
361             parent_dir[last_component - true_name + 1] = 0;
362             last_component++;   /*Skip the slash*/
363
364 #ifdef WIN32
365             if (!InAFS(parent_dir)) {
366                 const char * nbname = NetbiosName();
367                 int len = (int)strlen(nbname);
368
369                 if (parent_dir[0] == '\\' && parent_dir[1] == '\\' &&
370                     parent_dir[len+2] == '\\' &&
371                     parent_dir[len+3] == '\0' &&
372                     !strnicmp(nbname,&parent_dir[2],len))
373                 {
374                     sprintf(parent_dir,"\\\\%s\\all\\", nbname);
375                 }
376             }
377 #endif
378         }
379         else {
380             /*
381              * No slash appears in the given file name.  Set parent_dir to the current
382              * directory, and the last component as the given name.
383              */
384             fs_ExtractDriveLetter(true_name, parent_dir);
385             strcat(parent_dir, ".");
386             last_component = true_name;
387             fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
388         }
389
390         if (strcmp(last_component, ".") == 0 || strcmp(last_component, "..") == 0) {
391             fprintf(stderr,"%s: you may not use '.' or '..' as the last component\n", pn);
392             fprintf(stderr,"%s: of a name in the 'symlink list' command.\n", pn);
393             error = 1;
394             continue;
395         }
396         blob.in = last_component;
397         blob.in_size = (long)strlen(last_component)+1;
398         blob.out_size = MAXSIZE;
399         blob.out = space;
400         memset(space, 0, MAXSIZE);
401
402         code = pioctl(parent_dir, VIOC_LISTSYMLINK, &blob, 1);
403
404         if (code == 0)
405             printf("'%s' is a %ssymlink to '%s'\n",
406                    ti->data,
407                    (thru_symlink ? "symbolic link, leading to a " : ""),
408                    space);
409
410         else {
411             error = 1;
412             if (errno == EINVAL)
413                 fprintf(stderr,"'%s' is not a symlink.\n",
414                        ti->data);
415             else {
416                 Die(errno, (ti->data ? ti->data : parent_dir));
417             }
418         }
419     }
420     return error;
421 }
422
423 static MakeLinkCmd(as)
424 register struct cmd_syndesc *as; {
425     register afs_int32 code;
426     struct ViceIoctl blob;
427     char * parent;
428     char path[1024] = "";
429
430     strcpy(path, as->parms[0].items->data);
431     parent = Parent(path);
432
433     if (!InAFS(parent)) {
434 #ifdef WIN32
435         const char * nbname = NetbiosName();
436         int len = (int)strlen(nbname);
437
438         if (parent[0] == '\\' && parent[1] == '\\' &&
439             parent[len+2] == '\\' &&
440             parent[len+3] == '\0' &&
441             !strnicmp(nbname,&parent[2],len))
442         {
443             sprintf(path,"%sall\\%s", parent, &as->parms[0].items->data[strlen(parent)]);
444             parent = Parent(path);
445             if (!InAFS(parent)) {
446                 fprintf(stderr,"%s: symlinks must be created within the AFS file system\n", pn);
447                 return 1;
448             }
449         } else 
450 #endif
451         {
452             fprintf(stderr,"%s: symlinks must be created within the AFS file system\n", pn);
453             return 1;
454         }
455     }
456
457 #ifdef WIN32
458     if ( IsFreelanceRoot(parent) && !IsAdmin() ) {
459         fprintf(stderr,"%s: Only AFS Client Administrators may alter the root.afs volume\n", pn);
460         return 1;
461     }
462
463     /* create symlink with a special pioctl for Windows NT, since it doesn't
464      * have a symlink system call.
465      */
466     blob.out_size = 0;
467     blob.in_size = 1 + (long)strlen(as->parms[1].items->data);
468     blob.in = as->parms[1].items->data;
469     blob.out = NULL;
470     code = pioctl(path, VIOC_SYMLINK, &blob, 0);
471 #else /* not WIN32 */
472     code = symlink(as->parms[1].items->data, path);
473 #endif /* not WIN32 */
474     if (code) {
475         Die(errno, as->parms[0].items->data);
476         return 1;
477     }
478     return 0;
479 }
480
481 /*
482  * Delete AFS symlinks.  Variables are used as follows:
483  *       tbuffer: Set to point to the null-terminated directory name of the
484  *          symlink (or ``.'' if none is provided)
485  *      tp: Set to point to the actual name of the symlink to nuke.
486  */
487 static RemoveLinkCmd(as)
488 register struct cmd_syndesc *as; {
489     register afs_int32 code=0;
490     struct ViceIoctl blob;
491     register struct cmd_item *ti;
492     char tbuffer[1024];
493     char lsbuffer[1024];
494     register char *tp;
495     
496     for(ti=as->parms[0].items; ti; ti=ti->next) {
497         /* once per file */
498         tp = (char *) strrchr(ti->data, '\\');
499         if (!tp)
500             tp = (char *) strrchr(ti->data, '/');
501         if (tp) {
502             strncpy(tbuffer, ti->data, code=(afs_int32)(tp-ti->data+1));  /* the dir name */
503             tbuffer[code] = 0;
504             tp++;   /* skip the slash */
505
506 #ifdef WIN32
507             if (!InAFS(tbuffer)) {
508                 const char * nbname = NetbiosName();
509                 int len = (int)strlen(nbname);
510
511                 if (tbuffer[0] == '\\' && tbuffer[1] == '\\' &&
512                      tbuffer[len+2] == '\\' &&
513                      tbuffer[len+3] == '\0' &&
514                      !strnicmp(nbname,&tbuffer[2],len))
515                 {
516                     sprintf(tbuffer,"\\\\%s\\all\\", nbname);
517                 }
518             }
519 #endif
520         }
521         else {
522             fs_ExtractDriveLetter(ti->data, tbuffer);
523             strcat(tbuffer, ".");
524             tp = ti->data;
525             fs_StripDriveLetter(tp, tp, 0);
526         }
527         blob.in = tp;
528         blob.in_size = (int)strlen(tp)+1;
529         blob.out = lsbuffer;
530         blob.out_size = sizeof(lsbuffer);
531         code = pioctl(tbuffer, VIOC_LISTSYMLINK, &blob, 0);
532         if (code) {
533             if (errno == EINVAL)
534                 fprintf(stderr,"symlink: '%s' is not a symlink.\n", ti->data);
535             else {
536                 Die(errno, ti->data);
537             }
538             continue;   /* don't bother trying */
539         }
540
541         if ( IsFreelanceRoot(tbuffer) && !IsAdmin() ) {
542             fprintf(stderr,"symlink: Only AFS Client Administrators may alter the root.afs volume\n");
543             code = 1;
544             continue;   /* skip */
545         }
546
547         blob.out_size = 0;
548         blob.in = tp;
549         blob.in_size = (long)strlen(tp)+1;
550         code = pioctl(tbuffer, VIOC_DELSYMLINK, &blob, 0);
551         if (code) {
552             Die(errno, ti->data);
553         }
554     }
555     return code;
556 }
557
558 static    struct ViceIoctl gblob;
559 static int debug = 0;
560
561 main(argc, argv)
562 int argc;
563 char **argv; {
564     register afs_int32 code;
565     register struct cmd_syndesc *ts;
566     
567 #ifdef  AFS_AIX32_ENV
568     /*
569      * The following signal action for AIX is necessary so that in case of a 
570      * crash (i.e. core is generated) we can include the user's data section 
571      * in the core dump. Unfortunately, by default, only a partial core is
572      * generated which, in many cases, isn't too useful.
573      */
574     struct sigaction nsa;
575     
576     sigemptyset(&nsa.sa_mask);
577     nsa.sa_handler = SIG_DFL;
578     nsa.sa_flags = SA_FULLDUMP;
579     sigaction(SIGSEGV, &nsa, NULL);
580 #endif
581
582 #ifdef WIN32
583     WSADATA WSAjunk;
584     WSAStartup(0x0101, &WSAjunk);
585 #endif /* WIN32 */
586
587     /* try to find volume location information */
588     
589
590     osi_Init();
591
592     ts = cmd_CreateSyntax("list", ListLinkCmd, 0, "list symlink");    
593     cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
594     
595     ts = cmd_CreateSyntax("make", MakeLinkCmd, 0, "make symlink");
596     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name");
597     cmd_AddParm(ts, "-to", CMD_SINGLE, 0, "target");
598
599     ts = cmd_CreateSyntax("remove", RemoveLinkCmd, 0, "remove symlink");
600     cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
601     cmd_CreateAlias(ts, "rm");
602
603     code = cmd_Dispatch(argc, argv);
604
605 #ifndef WIN32
606     if (rxInitDone) rx_Finalize();
607 #endif /* not WIN32 */
608     
609     return code;
610 }
611
612 void Die(code, filename)
613     int   code;
614     char *filename;
615 { /*Die*/
616
617     if (code == EINVAL) {
618         if (filename)
619             fprintf(stderr,"%s: Invalid argument; it is possible that %s is not in AFS.\n", pn, filename);
620         else fprintf(stderr,"%s: Invalid argument.\n", pn);
621     }
622     else if (code == ENOENT) {
623         if (filename) fprintf(stderr,"%s: File '%s' doesn't exist\n", pn, filename);
624         else fprintf(stderr,"%s: no such file returned\n", pn);
625     }
626     else if (code == EROFS)  fprintf(stderr,"%s: You can not change a backup or readonly volume\n", pn);
627     else if (code == EACCES || code == EPERM) {
628         if (filename) fprintf(stderr,"%s: You don't have the required access rights on '%s'\n", pn, filename);
629         else fprintf(stderr,"%s: You do not have the required rights to do this operation\n", pn);
630     }
631     else if (code == ENODEV) {
632         fprintf(stderr,"%s: AFS service may not have started.\n", pn);
633     }
634     else if (code == ESRCH) {
635         fprintf(stderr,"%s: Cell name not recognized.\n", pn);
636     }
637     else if (code == EPIPE) {
638         fprintf(stderr,"%s: Volume name or ID not recognized.\n", pn);
639     }
640     else if (code == EFBIG) {
641         fprintf(stderr,"%s: Cache size too large.\n", pn);
642     }
643     else if (code == ETIMEDOUT) {
644         if (filename)
645             fprintf(stderr,"%s:'%s': Connection timed out", pn, filename);
646         else
647             fprintf(stderr,"%s: Connection timed out", pn);
648     }
649     else {
650         if (filename) fprintf(stderr,"%s:'%s'", pn, filename);
651         else fprintf(stderr,"%s", pn);
652 #ifdef WIN32
653         fprintf(stderr, ": code 0x%x\n", code);
654 #else /* not WIN32 */
655         fprintf(stderr,": %s\n", afs_error_message(code));
656 #endif /* not WIN32 */
657     }
658 } /*Die*/