Windows: Implement cm_TGTLifeTime()
[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 <afsconfig.h>
11 #include <afs/param.h>
12 #include <roken.h>
13
14 #include <afs/stds.h>
15 #include <afs/com_err.h>
16 #include <afs/afs_consts.h>
17
18 #include <windows.h>
19 #include <stdlib.h>
20 #include <malloc.h>
21 #include <string.h>
22 #include <strsafe.h>
23 #include <stdio.h>
24 #include <time.h>
25 #include <winsock2.h>
26 #include <errno.h>
27 #include <assert.h>
28
29 #include <osi.h>
30 #include <afsint.h>
31 #include <WINNT\afsreg.h>
32
33 #include "fs_utils.h"
34 #include "cmd.h"
35 #include "afsd.h"
36 #include "cm_ioctl.h"
37
38 #define MAXNAME 100
39 #define MAXINSIZE 1300    /* pioctl complains if data is larger than this */
40
41 static char space[AFS_PIOCTL_MAXSIZE];
42
43 static char pn[] = "symlink";
44 static int rxInitDone = 0;
45
46 static int
47 ListLinkCmd(struct cmd_syndesc *as, void *arock)
48 {
49     afs_int32 code;
50     struct ViceIoctl blob;
51     int error;
52     struct cmd_item *ti;
53     char orig_name[1024];               /* Original name */
54     char true_name[1024];               /*``True'' dirname (e.g., symlink target)*/
55     char parent_dir[1024];              /*Parent directory of true name*/
56     char *last_component;               /*Last component of true name*/
57     cm_ioctlQueryOptions_t options;
58
59     error = 0;
60     for(ti=as->parms[0].items; ti; ti=ti->next) {
61         cm_fid_t fid;
62         afs_uint32 filetype;
63
64         /* once per file */
65         if( FAILED(StringCbCopy(orig_name, sizeof(orig_name), ti->data))) {
66             fprintf (stderr, "ListLinkCmd - input path too long");
67             error = 1;
68             continue;
69         }
70
71         if( FAILED(StringCbCopy(true_name, sizeof(true_name), ti->data))) {
72             fprintf (stderr, "ListLinkCmd - input path too long");
73             error = 1;
74             continue;
75         }
76
77         /*
78          * Find rightmost slash, if any.
79          */
80         last_component = (char *) strrchr(true_name, '\\');
81         if (!last_component)
82             last_component = (char *) strrchr(true_name, '/');
83         if (last_component) {
84             /*
85              * Found it.  Designate everything before it as the parent directory,
86              * everything after it as the final component.  (explicitly relying
87              * on behavior of strncpy in this case.)
88              */
89             strncpy(parent_dir, true_name, last_component - true_name + 1);
90             parent_dir[last_component - true_name + 1] = 0;
91             last_component++;   /*Skip the slash*/
92
93             /*
94              * The following hack converts \\afs\foobar to \\afs\all\foobar.
95              * However, this hack should no longer be required as the pioctl()
96              * interface handles this conversion for us.
97              */
98             if (!fs_InAFS(parent_dir)) {
99                 const char * nbname = fs_NetbiosName();
100                 size_t len = strlen(nbname);
101
102                 if (parent_dir[0] == '\\' && parent_dir[1] == '\\' &&
103                     parent_dir[len+2] == '\\' &&
104                     parent_dir[len+3] == '\0' &&
105                     !strnicmp(nbname,&parent_dir[2],len))
106                 {
107                     if( FAILED(StringCbPrintf(parent_dir, sizeof(parent_dir), "\\\\%s\\all\\", nbname))) {
108                         fprintf(stderr, "parent_dir cannot be populated");
109                         error = 1;
110                         continue;
111                     }
112                 } else {
113                     fprintf(stderr,"'%s' is not in AFS.\n", parent_dir);
114                     error = 1;
115                     continue;
116                 }
117             }
118         }
119         else
120         {
121             /*
122              * No slash appears in the given file name.  Set parent_dir to the current
123              * directory, and the last component as the given name.
124              */
125             fs_ExtractDriveLetter(true_name, parent_dir);
126             if( FAILED(StringCbCat(parent_dir, sizeof(parent_dir), "."))) {
127                 fprintf (stderr, "parent_dir - not enough space");
128                 exit(1);
129             }
130             last_component = true_name;
131             fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
132         }
133
134         if (strcmp(last_component, ".") == 0 || strcmp(last_component, "..") == 0) {
135             fprintf(stderr,"%s: you may not use '.' or '..' as the last component\n", pn);
136             fprintf(stderr,"%s: of a name in the 'symlink list' command.\n", pn);
137             error = 1;
138             continue;
139         }
140
141         /* Check the object type */
142         memset(&fid, 0, sizeof(fid));
143         memset(&options, 0, sizeof(options));
144         filetype = 0;
145         options.size = sizeof(options);
146         options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
147         options.literal = 1;
148         blob.in_size = options.size;    /* no variable length data */
149         blob.in = &options;
150
151         blob.out_size = sizeof(cm_fid_t);
152         blob.out = (char *) &fid;
153         if (0 == pioctl_utf8(orig_name, VIOCGETFID, &blob, 1) &&
154             blob.out_size == sizeof(cm_fid_t)) {
155             options.field_flags |= CM_IOCTL_QOPTS_FIELD_FID;
156             options.fid = fid;
157         } else {
158             fs_Die(errno, ti->data);
159             error = 1;
160             continue;
161         }
162
163         blob.out_size = sizeof(filetype);
164         blob.out = &filetype;
165
166         code = pioctl_utf8(orig_name, VIOC_GETFILETYPE, &blob, 1);
167         if (code || blob.out_size != sizeof(filetype)) {
168             fs_Die(errno, ti->data);
169             error = 1;
170             continue;
171         }
172
173         if (filetype != 3 /* symlink */) {
174             fprintf(stderr,"'%s' is a %s not a Symlink.\n",
175                     orig_name, fs_filetypestr(filetype));
176             error = 1;
177             continue;
178         }
179
180         blob.in = last_component;
181         blob.in_size = (long)strlen(last_component)+1;
182
183         /* Now determine the symlink target */
184         blob.out_size = AFS_PIOCTL_MAXSIZE;
185         blob.out = space;
186         memset(space, 0, AFS_PIOCTL_MAXSIZE);
187
188         code = pioctl_utf8(parent_dir, VIOC_LISTSYMLINK, &blob, 1);
189         if (code == 0)
190             printf("'%s' is a symlink to '%.*s'\n",
191                    orig_name,
192                    blob.out_size,
193                    space);
194         else {
195             error = 1;
196             if (errno == EINVAL)
197                 fprintf(stderr,"'%s' is not a symlink.\n",
198                        ti->data);
199             else {
200                 fs_Die(errno, (ti->data ? ti->data : parent_dir));
201             }
202         }
203     }
204     return error;
205 }
206
207 static int
208 MakeLinkCmd(struct cmd_syndesc *as, void *arock)
209 {
210     afs_int32 code;
211     struct ViceIoctl blob;
212     char * parent;
213     char link[1024] = "";
214     char target_buf[1024] = "";
215     char *target = target_buf;
216     const char * nbname;
217     int nblen;
218
219     if( FAILED(StringCbCopy(link, sizeof(link), as->parms[0].items->data))) {
220         fprintf (stderr, "MakeLinkCmd - link path too long");
221         exit(1);
222     }
223     if( FAILED(StringCbCopy(target, sizeof(target_buf), as->parms[1].items->data))) {
224         fprintf (stderr, "MakeLinkCmd - target path too long");
225         exit(1);
226     }
227     parent = fs_GetParent(link);
228
229     nbname = fs_NetbiosName();
230     nblen = (int)strlen(nbname);
231
232     if (!fs_InAFS(parent)) {
233         if (parent[0] == '\\' && parent[1] == '\\' &&
234             (parent[nblen+2] == '\\' && parent[nblen+3] == '\0' || parent[nblen+2] == '\0') &&
235             !strnicmp(nbname,&parent[2],nblen))
236         {
237             if( FAILED(StringCbPrintf(link, sizeof(link),
238                                       "%s%sall%s", parent, parent[nblen+2]?"":"\\",
239                                       &as->parms[0].items->data[strlen(parent)]))) {
240                 fprintf(stderr, "link cannot be populated\n");
241                 exit(1);
242             }
243             parent = fs_GetParent(link);
244         } else {
245             fprintf(stderr,"%s: symlinks must be created within the AFS file system\n", pn);
246             return 1;
247         }
248     }
249
250     if ( fs_IsFreelanceRoot(parent) && !fs_IsAdmin() ) {
251         fprintf(stderr,"%s: Only AFS Client Administrators may alter the root.afs volume\n", pn);
252         return 1;
253     }
254
255     /*
256      * Fix up the target path to conform to unix notation
257      * if it is a UNC path to the AFS server name.
258      */
259     if (target[0] == '\\' && target[1] == '\\' &&
260         target[nblen+2] == '\\' &&
261                 !strnicmp(nbname,&target[2],nblen))
262     {
263         char *p;
264         for ( p=target; *p; p++) {
265             if (*p == '\\')
266                 *p = '/';
267         }
268         target++;
269     }
270     fprintf(stderr, "Creating symlink [%s] to [%s]\n", link, target);
271
272     /* create symlink with a special pioctl for Windows NT, since it doesn't
273      * have a symlink system call.
274      */
275     blob.out_size = 0;
276     blob.in_size = 1 + (long)strlen(target);
277     blob.in = target;
278     blob.out = NULL;
279     code = pioctl_utf8(link, VIOC_SYMLINK, &blob, 0);
280     if (code) {
281         fs_Die(errno, as->parms[0].items->data);
282         return 1;
283     }
284     return 0;
285 }
286
287 /*
288  * Delete AFS symlinks.  Variables are used as follows:
289  *       tbuffer: Set to point to the null-terminated directory name of the
290  *          symlink (or ``.'' if none is provided)
291  *      tp: Set to point to the actual name of the symlink to nuke.
292  */
293 static int
294 RemoveLinkCmd(struct cmd_syndesc *as, void *arock)
295 {
296     afs_int32 code=0;
297     struct ViceIoctl blob;
298     struct cmd_item *ti;
299     char tbuffer[1024];
300     char lsbuffer[1024];
301     char *tp;
302
303     for(ti=as->parms[0].items; ti; ti=ti->next) {
304         /* once per file */
305         tp = (char *) strrchr(ti->data, '\\');
306         if (!tp)
307             tp = (char *) strrchr(ti->data, '/');
308         if (tp) {
309             strncpy(tbuffer, ti->data, code=(afs_int32)(tp-ti->data+1));  /* the dir name */
310             tbuffer[code] = 0;
311             tp++;   /* skip the slash */
312
313             if (!fs_InAFS(tbuffer)) {
314                 const char * nbname = fs_NetbiosName();
315                 int len = (int)strlen(nbname);
316
317                 if (tbuffer[0] == '\\' && tbuffer[1] == '\\' &&
318                      tbuffer[len+2] == '\\' &&
319                      tbuffer[len+3] == '\0' &&
320                      !strnicmp(nbname,&tbuffer[2],len))
321                 {
322                     if( FAILED(StringCbPrintf(tbuffer, sizeof(tbuffer),
323                                               "\\\\%s\\all\\", nbname))) {
324                         fprintf( stderr, "tbuffer cannot be populated\n");
325                         exit(1);
326                     }
327                 } else {
328                     fprintf(stderr,"'%s' is not in AFS.\n", tbuffer);
329                     code = 1;
330                     continue;
331                 }
332             }
333         }
334         else
335         {
336             fs_ExtractDriveLetter(ti->data, tbuffer);
337             if( FAILED(StringCbCat(tbuffer, sizeof(tbuffer), "."))) {
338                 fprintf (stderr, "tbuffer - not enough space");
339                 exit(1);
340             }
341             tp = ti->data;
342             fs_StripDriveLetter(tp, tp, strlen(tp) + 1);
343         }
344         blob.in = tp;
345         blob.in_size = (int)strlen(tp)+1;
346         blob.out = lsbuffer;
347         blob.out_size = sizeof(lsbuffer);
348         code = pioctl_utf8(tbuffer, VIOC_LISTSYMLINK, &blob, 0);
349         if (code) {
350             if (errno == EINVAL)
351                 fprintf(stderr,"symlink: '%s' is not a symlink.\n", ti->data);
352             else {
353                 fs_Die(errno, ti->data);
354             }
355             continue;   /* don't bother trying */
356         }
357
358         if ( fs_IsFreelanceRoot(tbuffer) && !fs_IsAdmin() ) {
359             fprintf(stderr,"symlink: Only AFS Client Administrators may alter the root.afs volume\n");
360             code = 1;
361             continue;   /* skip */
362         }
363
364         blob.out_size = 0;
365         blob.in = tp;
366         blob.in_size = (long)strlen(tp)+1;
367         code = pioctl_utf8(tbuffer, VIOC_DELSYMLINK, &blob, 0);
368         if (code) {
369             fs_Die(errno, ti->data);
370         }
371     }
372     return code;
373 }
374
375 static struct ViceIoctl gblob;
376 static int debug = 0;
377
378 int
379 wmain(int argc, wchar_t **wargv)
380 {
381     afs_int32 code;
382     struct cmd_syndesc *ts;
383     char ** argv;
384
385     WSADATA WSAjunk;
386     WSAStartup(0x0101, &WSAjunk);
387
388     fs_SetProcessName(pn);
389
390     /* try to find volume location information */
391
392     argv = fs_MakeUtf8Cmdline(argc, wargv);
393
394     osi_Init();
395
396     ts = cmd_CreateSyntax("list", ListLinkCmd, NULL, "list symlink");
397     cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
398
399     ts = cmd_CreateSyntax("make", MakeLinkCmd, NULL, "make symlink");
400     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name");
401     cmd_AddParm(ts, "-to", CMD_SINGLE, 0, "target");
402
403     ts = cmd_CreateSyntax("remove", RemoveLinkCmd, NULL, "remove symlink");
404     cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
405     cmd_CreateAlias(ts, "rm");
406
407     code = cmd_Dispatch(argc, argv);
408
409     fs_FreeUtf8CmdLine(argc, argv);
410
411     return code;
412 }