windows-freelance-symlink-20041027
[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
22 #include <osi.h>
23 #include <afsint.h>
24
25 typedef long afs_int32;
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(apath)
63 register char *apath; {
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)) return 0;
74     }
75     return 1;
76 }
77
78 /* return a static pointer to a buffer */
79 static char *Parent(apath)
80 char *apath; {
81     register char *tp;
82     strcpy(tspace, apath);
83     tp = strrchr(tspace, '\\');
84     if (tp) {
85         *(tp+1) = 0;    /* lv trailing slash so Parent("k:\foo") is "k:\" not "k:" */
86     }
87     else {
88         fs_ExtractDriveLetter(apath, tspace);
89         strcat(tspace, ".");
90     }
91     return tspace;
92 }
93
94
95 static ListLinkCmd(as)
96 register struct cmd_syndesc *as; {
97     register afs_int32 code;
98     struct ViceIoctl blob;
99     int error;
100     register struct cmd_item *ti;
101     char orig_name[1024];               /*Original name, may be modified*/
102     char true_name[1024];               /*``True'' dirname (e.g., symlink target)*/
103     char parent_dir[1024];              /*Parent directory of true name*/
104     register char *last_component;      /*Last component of true name*/
105 #ifndef WIN32
106     struct stat statbuff;               /*Buffer for status info*/
107 #endif /* not WIN32 */
108 #ifndef WIN32
109     int link_chars_read;                /*Num chars read in readlink()*/
110 #endif /* not WIN32 */
111     int thru_symlink;                   /*Did we get to a mount point via a symlink?*/
112     
113     error = 0;
114     for(ti=as->parms[0].items; ti; ti=ti->next) {
115         /* once per file */
116         thru_symlink = 0;
117 #ifdef WIN32
118         strcpy(orig_name, ti->data);
119 #else /* not WIN32 */
120         sprintf(orig_name, "%s%s",
121                 (ti->data[0] == '/') ? "" : "./",
122                 ti->data);
123 #endif /* not WIN32 */
124
125 #ifndef WIN32
126         if (lstat(orig_name, &statbuff) < 0) {
127             /* if lstat fails, we should still try the pioctl, since it
128                 may work (for example, lstat will fail, but pioctl will
129                     work if the volume of offline (returning ENODEV). */
130             statbuff.st_mode = S_IFDIR; /* lie like pros */
131         }
132
133         /*
134          * The lstat succeeded.  If the given file is a symlink, substitute
135          * the file name with the link name.
136          */
137         if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
138             thru_symlink = 1;
139             /*
140              * Read name of resolved file.
141              */
142             link_chars_read = readlink(orig_name, true_name, 1024);
143             if (link_chars_read <= 0) {
144                 fprintf(stderr,"%s: Can't read target name for '%s' symbolic link!\n",
145                        pn, orig_name);
146                 exit(1);
147             }
148
149             /*
150              * Add a trailing null to what was read, bump the length.
151              */
152             true_name[link_chars_read++] = 0;
153
154             /*
155              * If the symlink is an absolute pathname, we're fine.  Otherwise, we
156              * have to create a full pathname using the original name and the
157              * relative symlink name.  Find the rightmost slash in the original
158              * name (we know there is one) and splice in the symlink value.
159              */
160             if (true_name[0] != '\\') {
161                 last_component = (char *) strrchr(orig_name, '\\');
162                 strcpy(++last_component, true_name);
163                 strcpy(true_name, orig_name);
164             }
165         }
166         else
167             strcpy(true_name, orig_name);
168 #else   /* WIN32 */
169         strcpy(true_name, orig_name);
170 #endif /* WIN32 */
171
172         /*
173          * Find rightmost slash, if any.
174          */
175         last_component = (char *) strrchr(true_name, '\\');
176         if (!last_component)
177             last_component = (char *) strrchr(true_name, '/');
178         if (last_component) {
179             /*
180              * Found it.  Designate everything before it as the parent directory,
181              * everything after it as the final component.
182              */
183             strncpy(parent_dir, true_name, last_component - true_name + 1);
184             parent_dir[last_component - true_name + 1] = 0;
185             last_component++;   /*Skip the slash*/
186         }
187         else {
188             /*
189              * No slash appears in the given file name.  Set parent_dir to the current
190              * directory, and the last component as the given name.
191              */
192             fs_ExtractDriveLetter(true_name, parent_dir);
193             strcat(parent_dir, ".");
194             last_component = true_name;
195             fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
196         }
197
198         if (strcmp(last_component, ".") == 0 || strcmp(last_component, "..") == 0) {
199             fprintf(stderr,"%s: you may not use '.' or '..' as the last component\n", pn);
200             fprintf(stderr,"%s: of a name in the 'symlink list' command.\n", pn);
201             error = 1;
202             continue;
203         }
204         blob.in = last_component;
205         blob.in_size = strlen(last_component)+1;
206         blob.out_size = MAXSIZE;
207         blob.out = space;
208         memset(space, 0, MAXSIZE);
209
210         code = pioctl(parent_dir, VIOC_LISTSYMLINK, &blob, 1);
211
212         if (code == 0)
213             printf("'%s' is a %ssymlink to '%s'\n",
214                    ti->data,
215                    (thru_symlink ? "symbolic link, leading to a " : ""),
216                    space);
217
218         else {
219             error = 1;
220             if (errno == EINVAL)
221                 fprintf(stderr,"'%s' is not a symlink.\n",
222                        ti->data);
223             else {
224                 Die(errno, (ti->data ? ti->data : parent_dir));
225             }
226         }
227     }
228     return error;
229 }
230
231 static MakeLinkCmd(as)
232 register struct cmd_syndesc *as; {
233     register afs_int32 code;
234     struct ViceIoctl blob;
235 #ifdef WIN32
236     char cm_NetbiosName[MAX_NB_NAME_LENGTH] = "";
237     BOOL isGateway = FALSE;
238     lana_number_t lanaNum;
239 #endif
240
241     if (!InAFS(Parent(as->parms[0].items->data))) {
242         fprintf(stderr,"%s: symlinks must be created within the AFS file system\n", pn);
243         return 1;
244     }
245
246     strcpy(space, as->parms[1].items->data);
247 #ifdef WIN32
248     /* create symlink with a special pioctl for Windows NT, since it doesn't
249      * have a symlink system call.
250      */
251
252     /* TODO: Code needs to go here to prevent the creation of symlinks
253      * in \\AFS\all when not in the "AFS Client Admins" group.
254      */
255
256     blob.out_size = 0;
257     blob.in_size = 1 + strlen(space);
258     blob.in = space;
259     blob.out = NULL;
260     code = pioctl(as->parms[0].items->data, VIOC_SYMLINK, &blob, 0);
261 #else /* not WIN32 */
262     code = symlink(space, as->parms[0].items->data);
263 #endif /* not WIN32 */
264     if (code) {
265         Die(errno, as->parms[0].items->data);
266         return 1;
267     }
268     return 0;
269 }
270
271 /*
272  * Delete AFS symlinks.  Variables are used as follows:
273  *       tbuffer: Set to point to the null-terminated directory name of the
274  *          symlink (or ``.'' if none is provided)
275  *      tp: Set to point to the actual name of the symlink to nuke.
276  */
277 static RemoveLinkCmd(as)
278 register struct cmd_syndesc *as; {
279     register afs_int32 code=0;
280     struct ViceIoctl blob;
281     register struct cmd_item *ti;
282     char tbuffer[1024];
283     char lsbuffer[1024];
284     register char *tp;
285     
286     for(ti=as->parms[0].items; ti; ti=ti->next) {
287         /* once per file */
288         tp = (char *) strrchr(ti->data, '\\');
289         if (!tp)
290             tp = (char *) strrchr(ti->data, '/');
291         if (tp) {
292             strncpy(tbuffer, ti->data, code=tp-ti->data+1);  /* the dir name */
293             tbuffer[code] = 0;
294             tp++;   /* skip the slash */
295         }
296         else {
297             fs_ExtractDriveLetter(ti->data, tbuffer);
298             strcat(tbuffer, ".");
299             tp = ti->data;
300             fs_StripDriveLetter(tp, tp, 0);
301         }
302         blob.in = tp;
303         blob.in_size = strlen(tp)+1;
304         blob.out = lsbuffer;
305         blob.out_size = sizeof(lsbuffer);
306         code = pioctl(tbuffer, VIOC_LISTSYMLINK, &blob, 0);
307         if (code) {
308             if (errno == EINVAL)
309                 fprintf(stderr,"fs: '%s' is not a symlink.\n", ti->data);
310             else {
311                 Die(errno, ti->data);
312             }
313             continue;   /* don't bother trying */
314         }
315         blob.out_size = 0;
316         blob.in = tp;
317         blob.in_size = strlen(tp)+1;
318         code = pioctl(tbuffer, VIOC_DELSYMLINK, &blob, 0);
319         if (code) {
320             Die(errno, ti->data);
321         }
322
323     }
324     return code;
325 }
326
327 static    struct ViceIoctl gblob;
328 static int debug = 0;
329
330 main(argc, argv)
331 int argc;
332 char **argv; {
333     register afs_int32 code;
334     register struct cmd_syndesc *ts;
335     
336 #ifdef  AFS_AIX32_ENV
337     /*
338      * The following signal action for AIX is necessary so that in case of a 
339      * crash (i.e. core is generated) we can include the user's data section 
340      * in the core dump. Unfortunately, by default, only a partial core is
341      * generated which, in many cases, isn't too useful.
342      */
343     struct sigaction nsa;
344     
345     sigemptyset(&nsa.sa_mask);
346     nsa.sa_handler = SIG_DFL;
347     nsa.sa_flags = SA_FULLDUMP;
348     sigaction(SIGSEGV, &nsa, NULL);
349 #endif
350
351 #ifdef WIN32
352     WSADATA WSAjunk;
353     WSAStartup(0x0101, &WSAjunk);
354 #endif /* WIN32 */
355
356     /* try to find volume location information */
357     
358
359     osi_Init();
360
361     ts = cmd_CreateSyntax("list", ListLinkCmd, 0, "list symlink");    
362     cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
363     
364     ts = cmd_CreateSyntax("make", MakeLinkCmd, 0, "make symlink");
365     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name");
366     cmd_AddParm(ts, "-to", CMD_SINGLE, 0, "target");
367
368     ts = cmd_CreateSyntax("rm", RemoveLinkCmd, 0, "remove symlink");
369     cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
370
371     code = cmd_Dispatch(argc, argv);
372
373 #ifndef WIN32
374     if (rxInitDone) rx_Finalize();
375 #endif /* not WIN32 */
376     
377     return code;
378 }
379
380 void Die(code, filename)
381     int   code;
382     char *filename;
383 { /*Die*/
384
385     if (code == EINVAL) {
386         if (filename)
387             fprintf(stderr,"%s: Invalid argument; it is possible that %s is not in AFS.\n", pn, filename);
388         else fprintf(stderr,"%s: Invalid argument.\n", pn);
389     }
390     else if (code == ENOENT) {
391         if (filename) fprintf(stderr,"%s: File '%s' doesn't exist\n", pn, filename);
392         else fprintf(stderr,"%s: no such file returned\n", pn);
393     }
394     else if (code == EROFS)  fprintf(stderr,"%s: You can not change a backup or readonly volume\n", pn);
395     else if (code == EACCES || code == EPERM) {
396         if (filename) fprintf(stderr,"%s: You don't have the required access rights on '%s'\n", pn, filename);
397         else fprintf(stderr,"%s: You do not have the required rights to do this operation\n", pn);
398     }
399     else if (code == ENODEV) {
400         fprintf(stderr,"%s: AFS service may not have started.\n", pn);
401     }
402     else if (code == ESRCH) {
403         fprintf(stderr,"%s: Cell name not recognized.\n", pn);
404     }
405     else if (code == EPIPE) {
406         fprintf(stderr,"%s: Volume name or ID not recognized.\n", pn);
407     }
408     else if (code == EFBIG) {
409         fprintf(stderr,"%s: Cache size too large.\n", pn);
410     }
411     else if (code == ETIMEDOUT) {
412         if (filename)
413             fprintf(stderr,"%s:'%s': Connection timed out", pn, filename);
414         else
415             fprintf(stderr,"%s: Connection timed out", pn);
416     }
417     else {
418         if (filename) fprintf(stderr,"%s:'%s'", pn, filename);
419         else fprintf(stderr,"%s", pn);
420 #ifdef WIN32
421         fprintf(stderr, ": code 0x%x\n", code);
422 #else /* not WIN32 */
423         fprintf(stderr,": %s\n", error_message(code));
424 #endif /* not WIN32 */
425     }
426 } /*Die*/