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>
30 #define MAXINSIZE 1300 /* pioctl complains if data is larger than this */
32 static char space[MAXSIZE];
33 static char tspace[1024];
36 static struct ubik_client *uclient;
37 #endif /* not WIN32 */
40 static char pn[] = "symlink";
41 static int rxInitDone = 0;
52 if (t >= 'A' && t <= 'Z') t += 0x20;
53 if (u >= 'A' && u <= 'Z') u += 0x20;
59 /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */
60 static int InAFS(apath)
61 register char *apath; {
62 struct ViceIoctl blob;
63 register afs_int32 code;
66 blob.out_size = MAXSIZE;
69 code = pioctl(apath, VIOC_FILE_CELL_NAME, &blob, 1);
71 if ((errno == EINVAL) || (errno == ENOENT)) return 0;
76 /* return a static pointer to a buffer */
77 static char *Parent(apath)
80 strcpy(tspace, apath);
81 tp = strrchr(tspace, '\\');
83 *(tp+1) = 0; /* lv trailing slash so Parent("k:\foo") is "k:\" not "k:" */
86 fs_ExtractDriveLetter(apath, tspace);
93 static ListLinkCmd(as)
94 register struct cmd_syndesc *as; {
95 register afs_int32 code;
96 struct ViceIoctl blob;
98 register struct cmd_item *ti;
99 char orig_name[1024]; /*Original name, may be modified*/
100 char true_name[1024]; /*``True'' dirname (e.g., symlink target)*/
101 char parent_dir[1024]; /*Parent directory of true name*/
102 register char *last_component; /*Last component of true name*/
104 struct stat statbuff; /*Buffer for status info*/
105 #endif /* not WIN32 */
107 int link_chars_read; /*Num chars read in readlink()*/
108 #endif /* not WIN32 */
109 int thru_symlink; /*Did we get to a mount point via a symlink?*/
112 for(ti=as->parms[0].items; ti; ti=ti->next) {
116 strcpy(orig_name, ti->data);
117 #else /* not WIN32 */
118 sprintf(orig_name, "%s%s",
119 (ti->data[0] == '/') ? "" : "./",
121 #endif /* not WIN32 */
124 if (lstat(orig_name, &statbuff) < 0) {
125 /* if lstat fails, we should still try the pioctl, since it
126 may work (for example, lstat will fail, but pioctl will
127 work if the volume of offline (returning ENODEV). */
128 statbuff.st_mode = S_IFDIR; /* lie like pros */
132 * The lstat succeeded. If the given file is a symlink, substitute
133 * the file name with the link name.
135 if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
138 * Read name of resolved file.
140 link_chars_read = readlink(orig_name, true_name, 1024);
141 if (link_chars_read <= 0) {
142 fprintf(stderr,"%s: Can't read target name for '%s' symbolic link!\n",
148 * Add a trailing null to what was read, bump the length.
150 true_name[link_chars_read++] = 0;
153 * If the symlink is an absolute pathname, we're fine. Otherwise, we
154 * have to create a full pathname using the original name and the
155 * relative symlink name. Find the rightmost slash in the original
156 * name (we know there is one) and splice in the symlink value.
158 if (true_name[0] != '\\') {
159 last_component = (char *) strrchr(orig_name, '\\');
160 strcpy(++last_component, true_name);
161 strcpy(true_name, orig_name);
165 strcpy(true_name, orig_name);
167 strcpy(true_name, orig_name);
171 * Find rightmost slash, if any.
173 last_component = (char *) strrchr(true_name, '\\');
175 last_component = (char *) strrchr(true_name, '/');
176 if (last_component) {
178 * Found it. Designate everything before it as the parent directory,
179 * everything after it as the final component.
181 strncpy(parent_dir, true_name, last_component - true_name + 1);
182 parent_dir[last_component - true_name + 1] = 0;
183 last_component++; /*Skip the slash*/
187 * No slash appears in the given file name. Set parent_dir to the current
188 * directory, and the last component as the given name.
190 fs_ExtractDriveLetter(true_name, parent_dir);
191 strcat(parent_dir, ".");
192 last_component = true_name;
193 fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
196 if (strcmp(last_component, ".") == 0 || strcmp(last_component, "..") == 0) {
197 fprintf(stderr,"%s: you may not use '.' or '..' as the last component\n", pn);
198 fprintf(stderr,"%s: of a name in the 'symlink list' command.\n", pn);
202 blob.in = last_component;
203 blob.in_size = strlen(last_component)+1;
204 blob.out_size = MAXSIZE;
206 memset(space, 0, MAXSIZE);
208 code = pioctl(parent_dir, VIOC_LISTSYMLINK, &blob, 1);
211 printf("'%s' is a %ssymlink to '%s'\n",
213 (thru_symlink ? "symbolic link, leading to a " : ""),
219 fprintf(stderr,"'%s' is not a symlink.\n",
222 Die(errno, (ti->data ? ti->data : parent_dir));
229 static MakeLinkCmd(as)
230 register struct cmd_syndesc *as; {
231 register afs_int32 code;
232 struct ViceIoctl blob;
234 if (!InAFS(Parent(as->parms[0].items->data))) {
235 fprintf(stderr,"%s: symlinks must be created within the AFS file system\n", pn);
239 strcpy(space, as->parms[1].items->data);
241 /* create symlink with a special pioctl for Windows NT, since it doesn't
242 * have a symlink system call.
245 /* TODO: Code needs to go here to prevent the creation of symlinks
246 * in \\AFS\all when not in the "AFS Client Admins" group.
250 blob.in_size = 1 + strlen(space);
253 code = pioctl(as->parms[0].items->data, VIOC_SYMLINK, &blob, 0);
254 #else /* not WIN32 */
255 code = symlink(space, as->parms[0].items->data);
256 #endif /* not WIN32 */
258 Die(errno, as->parms[0].items->data);
265 * Delete AFS symlinks. Variables are used as follows:
266 * tbuffer: Set to point to the null-terminated directory name of the
267 * symlink (or ``.'' if none is provided)
268 * tp: Set to point to the actual name of the symlink to nuke.
270 static RemoveLinkCmd(as)
271 register struct cmd_syndesc *as; {
272 register afs_int32 code=0;
273 struct ViceIoctl blob;
274 register struct cmd_item *ti;
279 for(ti=as->parms[0].items; ti; ti=ti->next) {
281 tp = (char *) strrchr(ti->data, '\\');
283 tp = (char *) strrchr(ti->data, '/');
285 strncpy(tbuffer, ti->data, code=tp-ti->data+1); /* the dir name */
287 tp++; /* skip the slash */
290 fs_ExtractDriveLetter(ti->data, tbuffer);
291 strcat(tbuffer, ".");
293 fs_StripDriveLetter(tp, tp, 0);
296 blob.in_size = strlen(tp)+1;
298 blob.out_size = sizeof(lsbuffer);
299 code = pioctl(tbuffer, VIOC_LISTSYMLINK, &blob, 0);
302 fprintf(stderr,"fs: '%s' is not a symlink.\n", ti->data);
304 Die(errno, ti->data);
306 continue; /* don't bother trying */
310 blob.in_size = strlen(tp)+1;
311 code = pioctl(tbuffer, VIOC_DELSYMLINK, &blob, 0);
313 Die(errno, ti->data);
320 static struct ViceIoctl gblob;
321 static int debug = 0;
326 register afs_int32 code;
327 register struct cmd_syndesc *ts;
331 * The following signal action for AIX is necessary so that in case of a
332 * crash (i.e. core is generated) we can include the user's data section
333 * in the core dump. Unfortunately, by default, only a partial core is
334 * generated which, in many cases, isn't too useful.
336 struct sigaction nsa;
338 sigemptyset(&nsa.sa_mask);
339 nsa.sa_handler = SIG_DFL;
340 nsa.sa_flags = SA_FULLDUMP;
341 sigaction(SIGSEGV, &nsa, NULL);
346 WSAStartup(0x0101, &WSAjunk);
349 /* try to find volume location information */
354 ts = cmd_CreateSyntax("list", ListLinkCmd, 0, "list symlink");
355 cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
357 ts = cmd_CreateSyntax("make", MakeLinkCmd, 0, "make symlink");
358 cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "name");
359 cmd_AddParm(ts, "-to", CMD_SINGLE, 0, "target");
361 ts = cmd_CreateSyntax("rm", RemoveLinkCmd, 0, "remove symlink");
362 cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
364 code = cmd_Dispatch(argc, argv);
367 if (rxInitDone) rx_Finalize();
368 #endif /* not WIN32 */
373 void Die(code, filename)
378 if (code == EINVAL) {
380 fprintf(stderr,"%s: Invalid argument; it is possible that %s is not in AFS.\n", pn, filename);
381 else fprintf(stderr,"%s: Invalid argument.\n", pn);
383 else if (code == ENOENT) {
384 if (filename) fprintf(stderr,"%s: File '%s' doesn't exist\n", pn, filename);
385 else fprintf(stderr,"%s: no such file returned\n", pn);
387 else if (code == EROFS) fprintf(stderr,"%s: You can not change a backup or readonly volume\n", pn);
388 else if (code == EACCES || code == EPERM) {
389 if (filename) fprintf(stderr,"%s: You don't have the required access rights on '%s'\n", pn, filename);
390 else fprintf(stderr,"%s: You do not have the required rights to do this operation\n", pn);
392 else if (code == ENODEV) {
393 fprintf(stderr,"%s: AFS service may not have started.\n", pn);
395 else if (code == ESRCH) {
396 fprintf(stderr,"%s: Cell name not recognized.\n", pn);
398 else if (code == EPIPE) {
399 fprintf(stderr,"%s: Volume name or ID not recognized.\n", pn);
401 else if (code == EFBIG) {
402 fprintf(stderr,"%s: Cache size too large.\n", pn);
404 else if (code == ETIMEDOUT) {
406 fprintf(stderr,"%s:'%s': Connection timed out", pn, filename);
408 fprintf(stderr,"%s: Connection timed out", pn);
411 if (filename) fprintf(stderr,"%s:'%s'", pn, filename);
412 else fprintf(stderr,"%s", pn);
414 fprintf(stderr, ": code 0x%x\n", code);
415 #else /* not WIN32 */
416 fprintf(stderr,": %s\n", error_message(code));
417 #endif /* not WIN32 */