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>
25 typedef long afs_int32;
32 #define MAXINSIZE 1300 /* pioctl complains if data is larger than this */
34 static char space[MAXSIZE];
35 static char tspace[1024];
38 static struct ubik_client *uclient;
39 #endif /* not WIN32 */
42 static char pn[] = "symlink";
43 static int rxInitDone = 0;
54 if (t >= 'A' && t <= 'Z') t += 0x20;
55 if (u >= 'A' && u <= 'Z') u += 0x20;
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;
68 blob.out_size = MAXSIZE;
71 code = pioctl(apath, VIOC_FILE_CELL_NAME, &blob, 1);
73 if ((errno == EINVAL) || (errno == ENOENT)) return 0;
78 /* return a static pointer to a buffer */
79 static char *Parent(apath)
82 strcpy(tspace, apath);
83 tp = strrchr(tspace, '\\');
85 *(tp+1) = 0; /* lv trailing slash so Parent("k:\foo") is "k:\" not "k:" */
88 fs_ExtractDriveLetter(apath, tspace);
95 static ListLinkCmd(as)
96 register struct cmd_syndesc *as; {
97 register afs_int32 code;
98 struct ViceIoctl blob;
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*/
106 struct stat statbuff; /*Buffer for status info*/
107 #endif /* not 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?*/
114 for(ti=as->parms[0].items; ti; ti=ti->next) {
118 strcpy(orig_name, ti->data);
119 #else /* not WIN32 */
120 sprintf(orig_name, "%s%s",
121 (ti->data[0] == '/') ? "" : "./",
123 #endif /* not 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 */
134 * The lstat succeeded. If the given file is a symlink, substitute
135 * the file name with the link name.
137 if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
140 * Read name of resolved file.
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",
150 * Add a trailing null to what was read, bump the length.
152 true_name[link_chars_read++] = 0;
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.
160 if (true_name[0] != '\\') {
161 last_component = (char *) strrchr(orig_name, '\\');
162 strcpy(++last_component, true_name);
163 strcpy(true_name, orig_name);
167 strcpy(true_name, orig_name);
169 strcpy(true_name, orig_name);
173 * Find rightmost slash, if any.
175 last_component = (char *) strrchr(true_name, '\\');
177 last_component = (char *) strrchr(true_name, '/');
178 if (last_component) {
180 * Found it. Designate everything before it as the parent directory,
181 * everything after it as the final component.
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*/
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.
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));
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);
204 blob.in = last_component;
205 blob.in_size = strlen(last_component)+1;
206 blob.out_size = MAXSIZE;
208 memset(space, 0, MAXSIZE);
210 code = pioctl(parent_dir, VIOC_LISTSYMLINK, &blob, 1);
213 printf("'%s' is a %ssymlink to '%s'\n",
215 (thru_symlink ? "symbolic link, leading to a " : ""),
221 fprintf(stderr,"'%s' is not a symlink.\n",
224 Die(errno, (ti->data ? ti->data : parent_dir));
231 static MakeLinkCmd(as)
232 register struct cmd_syndesc *as; {
233 register afs_int32 code;
234 struct ViceIoctl blob;
236 if (!InAFS(Parent(as->parms[0].items->data))) {
237 fprintf(stderr,"%s: symlinks must be created within the AFS file system\n", pn);
241 strcpy(space, as->parms[1].items->data);
243 /* create symlink with a special pioctl for Windows NT, since it doesn't
244 * have a symlink system call.
247 blob.in_size = 1 + strlen(space);
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 */
255 Die(errno, as->parms[0].items->data);
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.
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;
276 for(ti=as->parms[0].items; ti; ti=ti->next) {
278 tp = (char *) strrchr(ti->data, '\\');
280 tp = (char *) strrchr(ti->data, '/');
282 strncpy(tbuffer, ti->data, code=tp-ti->data+1); /* the dir name */
284 tp++; /* skip the slash */
287 fs_ExtractDriveLetter(ti->data, tbuffer);
288 strcat(tbuffer, ".");
290 fs_StripDriveLetter(tp, tp, 0);
293 blob.in_size = strlen(tp)+1;
295 blob.out_size = sizeof(lsbuffer);
296 code = pioctl(tbuffer, VIOC_LISTSYMLINK, &blob, 0);
299 fprintf(stderr,"fs: '%s' is not a symlink.\n", ti->data);
301 Die(errno, ti->data);
303 continue; /* don't bother trying */
307 blob.in_size = strlen(tp)+1;
308 code = pioctl(tbuffer, VIOC_DELSYMLINK, &blob, 0);
310 Die(errno, ti->data);
317 static struct ViceIoctl gblob;
318 static int debug = 0;
323 register afs_int32 code;
324 register struct cmd_syndesc *ts;
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.
333 struct sigaction nsa;
335 sigemptyset(&nsa.sa_mask);
336 nsa.sa_handler = SIG_DFL;
337 nsa.sa_flags = SA_FULLDUMP;
338 sigaction(SIGSEGV, &nsa, NULL);
343 WSAStartup(0x0101, &WSAjunk);
346 /* try to find volume location information */
351 ts = cmd_CreateSyntax("list", ListLinkCmd, 0, "list symlink");
352 cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
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");
358 ts = cmd_CreateSyntax("rm", RemoveLinkCmd, 0, "remove symlink");
359 cmd_AddParm(ts, "-name", CMD_LIST, 0, "name");
361 code = cmd_Dispatch(argc, argv);
364 if (rxInitDone) rx_Finalize();
365 #endif /* not WIN32 */
370 void Die(code, filename)
375 if (code == EINVAL) {
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);
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);
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);
389 else if (code == ENODEV) {
390 fprintf(stderr,"%s: AFS service may not have started.\n", pn);
392 else if (code == ESRCH) {
393 fprintf(stderr,"%s: Cell name not recognized.\n", pn);
395 else if (code == EPIPE) {
396 fprintf(stderr,"%s: Volume name or ID not recognized.\n", pn);
398 else if (code == EFBIG) {
399 fprintf(stderr,"%s: Cache size too large.\n", pn);
401 else if (code == ETIMEDOUT) {
403 fprintf(stderr,"%s:'%s': Connection timed out", pn, filename);
405 fprintf(stderr,"%s: Connection timed out", pn);
408 if (filename) fprintf(stderr,"%s:'%s'", pn, filename);
409 else fprintf(stderr,"%s", pn);
411 fprintf(stderr, ": code 0x%x\n", code);
412 #else /* not WIN32 */
413 fprintf(stderr,": %s\n", error_message(code));
414 #endif /* not WIN32 */