69fe625fd23b5a96cb0268755c6b51c95fdc1354
[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
236     if (!InAFS(Parent(as->parms[0].items->data))) {
237         fprintf(stderr,"%s: symlinks must be created within the AFS file system\n", pn);
238         return 1;
239     }
240
241     strcpy(space, as->parms[1].items->data);
242 #ifdef WIN32
243     /* create symlink with a special pioctl for Windows NT, since it doesn't
244      * have a symlink system call.
245      */
246     blob.out_size = 0;
247     blob.in_size = 1 + strlen(space);
248     blob.in = space;
249     blob.out = NULL;
250     code = pioctl(as->parms[0].items->data, VIOC_SYMLINK, &blob, 0);
251 #else /* not WIN32 */
252     code = symlink(space, as->parms[0].items->data);
253 #endif /* not WIN32 */
254     if (code) {
255         Die(errno, as->parms[0].items->data);
256         return 1;
257     }
258     return 0;
259 }
260
261 /*
262  * Delete AFS symlinks.  Variables are used as follows:
263  *       tbuffer: Set to point to the null-terminated directory name of the
264  *          symlink (or ``.'' if none is provided)
265  *      tp: Set to point to the actual name of the symlink to nuke.
266  */
267 static RemoveLinkCmd(as)
268 register struct cmd_syndesc *as; {
269     register afs_int32 code=0;
270     struct ViceIoctl blob;
271     register struct cmd_item *ti;
272     char tbuffer[1024];
273     char lsbuffer[1024];
274     register char *tp;
275     
276     for(ti=as->parms[0].items; ti; ti=ti->next) {
277         /* once per file */
278         tp = (char *) strrchr(ti->data, '\\');
279         if (!tp)
280             tp = (char *) strrchr(ti->data, '/');
281         if (tp) {
282             strncpy(tbuffer, ti->data, code=tp-ti->data+1);  /* the dir name */
283             tbuffer[code] = 0;
284             tp++;   /* skip the slash */
285         }
286         else {
287             fs_ExtractDriveLetter(ti->data, tbuffer);
288             strcat(tbuffer, ".");
289             tp = ti->data;
290             fs_StripDriveLetter(tp, tp, 0);
291         }
292         blob.in = tp;
293         blob.in_size = strlen(tp)+1;
294         blob.out = lsbuffer;
295         blob.out_size = sizeof(lsbuffer);
296         code = pioctl(tbuffer, VIOC_LISTSYMLINK, &blob, 0);
297         if (code) {
298             if (errno == EINVAL)
299                 fprintf(stderr,"fs: '%s' is not a symlink.\n", ti->data);
300             else {
301                 Die(errno, ti->data);
302             }
303             continue;   /* don't bother trying */
304         }
305         blob.out_size = 0;
306         blob.in = tp;
307         blob.in_size = strlen(tp)+1;
308         code = pioctl(tbuffer, VIOC_DELSYMLINK, &blob, 0);
309         if (code) {
310             Die(errno, ti->data);
311         }
312
313     }
314     return code;
315 }
316
317 static    struct ViceIoctl gblob;
318 static int debug = 0;
319
320 main(argc, argv)
321 int argc;
322 char **argv; {
323     register afs_int32 code;
324     register struct cmd_syndesc *ts;
325     
326 #ifdef  AFS_AIX32_ENV
327     /*
328      * The following signal action for AIX is necessary so that in case of a 
329      * crash (i.e. core is generated) we can include the user's data section 
330      * in the core dump. Unfortunately, by default, only a partial core is
331      * generated which, in many cases, isn't too useful.
332      */
333     struct sigaction nsa;
334     
335     sigemptyset(&nsa.sa_mask);
336     nsa.sa_handler = SIG_DFL;
337     nsa.sa_flags = SA_FULLDUMP;
338     sigaction(SIGSEGV, &nsa, NULL);
339 #endif
340
341 #ifdef WIN32
342     WSADATA WSAjunk;
343     WSAStartup(0x0101, &WSAjunk);
344 #endif /* WIN32 */
345
346     /* try to find volume location information */
347     
348
349     osi_Init();
350
351     ts = cmd_CreateSyntax("list", ListLinkCmd, 0, "list symlink");    
352     cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
353     
354     ts = cmd_CreateSyntax("make", MakeLinkCmd, 0, "make symlink");
355     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name");
356     cmd_AddParm(ts, "-to", CMD_SINGLE, 0, "target");
357
358     ts = cmd_CreateSyntax("rm", RemoveLinkCmd, 0, "remove symlink");
359     cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
360
361     code = cmd_Dispatch(argc, argv);
362
363 #ifndef WIN32
364     if (rxInitDone) rx_Finalize();
365 #endif /* not WIN32 */
366     
367     return code;
368 }
369
370 void Die(code, filename)
371     int   code;
372     char *filename;
373 { /*Die*/
374
375     if (code == EINVAL) {
376         if (filename)
377             fprintf(stderr,"%s: Invalid argument; it is possible that %s is not in AFS.\n", pn, filename);
378         else fprintf(stderr,"%s: Invalid argument.\n", pn);
379     }
380     else if (code == ENOENT) {
381         if (filename) fprintf(stderr,"%s: File '%s' doesn't exist\n", pn, filename);
382         else fprintf(stderr,"%s: no such file returned\n", pn);
383     }
384     else if (code == EROFS)  fprintf(stderr,"%s: You can not change a backup or readonly volume\n", pn);
385     else if (code == EACCES || code == EPERM) {
386         if (filename) fprintf(stderr,"%s: You don't have the required access rights on '%s'\n", pn, filename);
387         else fprintf(stderr,"%s: You do not have the required rights to do this operation\n", pn);
388     }
389     else if (code == ENODEV) {
390         fprintf(stderr,"%s: AFS service may not have started.\n", pn);
391     }
392     else if (code == ESRCH) {
393         fprintf(stderr,"%s: Cell name not recognized.\n", pn);
394     }
395     else if (code == EPIPE) {
396         fprintf(stderr,"%s: Volume name or ID not recognized.\n", pn);
397     }
398     else if (code == EFBIG) {
399         fprintf(stderr,"%s: Cache size too large.\n", pn);
400     }
401     else if (code == ETIMEDOUT) {
402         if (filename)
403             fprintf(stderr,"%s:'%s': Connection timed out", pn, filename);
404         else
405             fprintf(stderr,"%s: Connection timed out", pn);
406     }
407     else {
408         if (filename) fprintf(stderr,"%s:'%s'", pn, filename);
409         else fprintf(stderr,"%s", pn);
410 #ifdef WIN32
411         fprintf(stderr, ": code 0x%x\n", code);
412 #else /* not WIN32 */
413         fprintf(stderr,": %s\n", error_message(code));
414 #endif /* not WIN32 */
415     }
416 } /*Die*/