/* * Dissect the path and create corresponding superblock. Note that inname * can be changed and the result may point inside the original string. */ const char * vfs_s_get_path_mangle (struct vfs_class *me, char *inname, struct vfs_s_super **archive, int flags) { const char *retval; char *local, *op; const char *archive_name; int result = -1; struct vfs_s_super *super; void *cookie = NULL; archive_name = inname; vfs_split (inname, &local, &op); retval = (local) ? local : ""; if (MEDATA->archive_check) if (!(cookie = MEDATA->archive_check (me, archive_name, op))) return NULL; for (super = MEDATA->supers; super != NULL; super = super->next) { /* 0 == other, 1 == same, return it, 2 == other but stop scanning */ int i; if ((i = MEDATA->archive_same (me, super, archive_name, op, cookie))) { if (i == 1) goto return_success; else break; } } if (flags & FL_NO_OPEN) ERRNOR (EIO, NULL); super = vfs_s_new_super (me); result = MEDATA->open_archive (me, super, archive_name, op); if (result == -1) { vfs_s_free_super (me, super); ERRNOR (EIO, NULL); } if (!super->name) vfs_die ("You have to fill name\n"); if (!super->root) vfs_die ("You have to fill root inode\n"); vfs_s_insert_super (me, super); vfs_stamp_create (me, super); return_success: *archive = super; return retval; }
/* Returns fd of the open tar file */ static int tar_open_archive (vfs *me, char *name, vfs_s_super *archive) { int result, type; long size; mode_t mode; struct vfs_s_inode *root; result = mc_open (name, O_RDONLY); if (result == -1) { message_2s (1, MSG_ERROR, _("Couldn't open tar archive\n%s"), name); ERRNOR (ENOENT, -1); } archive->name = strdup (name); mc_stat (name, &(archive->u.tar.tarstat)); archive->u.tar.fd = -1; /* Find out the method to handle this tar file */ size = is_gunzipable (result, &type); mc_lseek (result, 0, SEEK_SET); if (size > 0) { char *s; mc_close( result ); s = copy_strings( archive->name, decompress_extension (type), NULL ); result = mc_open (s, O_RDONLY); if (result == -1) message_2s (1, MSG_ERROR, _("Couldn't open tar archive\n%s"), s); free(s); if (result == -1) ERRNOR (ENOENT, -1); } archive->u.tar.fd = result; mode = archive->u.tar.tarstat.st_mode & 07777; if (mode & 0400) mode |= 0100; if (mode & 0040) mode |= 0010; if (mode & 0004) mode |= 0001; mode |= S_IFDIR; root = vfs_s_new_inode (me, archive, &archive->u.tar.tarstat); root->st.st_mode = mode; root->u.tar.data_offset = -1; root->st.st_nlink++; root->st.st_dev = MEDATA->rdev++; vfs_s_add_dots (me, root, NULL); archive->root = root; return result; }
char * vfs_s_fullpath (struct vfs_class *me, struct vfs_s_inode *ino) { if (!ino->ent) ERRNOR (EAGAIN, NULL); if (!(MEDATA->flags & VFS_S_REMOTE)) { /* archives */ char *newpath; char *path = g_strdup (ino->ent->name); while (1) { ino = ino->ent->dir; if (ino == ino->super->root) break; newpath = g_strconcat (ino->ent->name, "/", path, (char *) NULL); g_free (path); path = newpath; } return path; } /* remote systems */ if ((!ino->ent->dir) || (!ino->ent->dir->ent)) return g_strdup (ino->ent->name); return g_strconcat (ino->ent->dir->ent->name, PATH_SEP_STR, ino->ent->name, (char *) NULL); }
static int tar_read (void *fh, char *buffer, int count) { off_t begin = FH->ino->u.tar.data_offset; int fd = FH_SUPER->u.tar.fd; vfs *me = FH_SUPER->me; if (mc_lseek (fd, begin + FH->pos, SEEK_SET) != begin + FH->pos) ERRNOR (EIO, -1); count = VFS_MIN(count, FH->ino->st.st_size - FH->pos); if ((count = mc_read (fd, buffer, count)) == -1) ERRNOR (errno, -1); FH->pos += count; return count; }
static int fish_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len) { struct vfs_s_super *super = FH_SUPER; int n = 0; len = MIN( fh->u.fish.total - fh->u.fish.got, len ); disable_interrupt_key(); while (len && ((n = read (SUP.sockr, buf, len))<0)) { if ((errno == EINTR) && !got_interrupt()) continue; break; } enable_interrupt_key(); if (n>0) fh->u.fish.got += n; if (n<0) fish_linear_abort(me, fh); if ((!n) && ((fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE))) ERRNOR (E_REMOTE, -1); ERRNOR (errno, n); }
static int fish_send_command(struct vfs_class *me, struct vfs_s_super *super, const char *cmd, int flags) { int r; r = fish_command (me, super, WAIT_REPLY, "%s", cmd); vfs_stamp_create (&vfs_fish_ops, super); if (r != COMPLETE) ERRNOR (E_REMOTE, -1); if (flags & OPT_FLUSH) vfs_s_invalidate(me, super); return 0; }
static int fish_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset) { char *name; char *quoted_name; if (offset) ERRNOR (E_NOTSUPP, 0); name = vfs_s_fullpath (me, fh->ino); if (!name) return 0; quoted_name = name_quote (name, 0); g_free (name); name = quoted_name; fh->u.fish.append = 0; offset = fish_command (me, FH_SUPER, WANT_STRING, "#RETR /%s\n" "if dd if=/%s of=/dev/null bs=1 count=1 2>/dev/null; then\n" "ls -ln /%s 2>/dev/null | (\n" "read p l u g s r\n" "echo \"$s\"\n" ")\n" "echo '### 100'\n" "cat /%s\n" "echo '### 200'\n" "else\n" "echo '### 500'\n" "fi\n", name, name, name, name ); g_free (name); if (offset != PRELIM) ERRNOR (E_REMOTE, 0); fh->linear = LS_LINEAR_OPEN; fh->u.fish.got = 0; #if SIZEOF_OFF_T > SIZEOF_INT if (sscanf( reply_str, "%llu", &fh->u.fish.total )!=1) #else if (sscanf( reply_str, "%u", &fh->u.fish.total )!=1) #endif ERRNOR (E_REMOTE, 0); return 1; }
/* If the entry is a symlink, find the entry for its target */ static struct vfs_s_entry * vfs_s_resolve_symlink (struct vfs_class *me, struct vfs_s_entry *entry, int follow) { char *linkname; char *fullname = NULL; struct vfs_s_entry *target; if (follow == LINK_NO_FOLLOW) return entry; if (follow == 0) ERRNOR (ELOOP, NULL); if (!entry) ERRNOR (ENOENT, NULL); if (!S_ISLNK (entry->ino->st.st_mode)) return entry; linkname = entry->ino->linkname; if (linkname == NULL) ERRNOR (EFAULT, NULL); /* make full path from relative */ if (*linkname != PATH_SEP) { char *fullpath = vfs_s_fullpath (me, entry->dir); if (fullpath) { fullname = g_strconcat (fullpath, "/", linkname, NULL); linkname = fullname; g_free (fullpath); } } target = (MEDATA->find_entry) (me, entry->dir->super->root, linkname, follow - 1, 0); g_free (fullname); return target; }
/* * Dissect the path and create corresponding superblock. Note that inname * can be changed and the result may point inside the original string. */ static char * extfs_get_path_mangle (struct vfs_class *me, char *inname, struct archive **archive, int do_not_open) { char *local, *op; const char *archive_name; int result = -1; struct archive *parc; int fstype; archive_name = inname; vfs_split (inname, &local, &op); fstype = extfs_which (me, op); if (fstype == -1) return NULL; if (!local) local = inname + strlen (inname); /* * All filesystems should have some local archive, at least * it can be '/'. */ for (parc = first_archive; parc != NULL; parc = parc->next) if (parc->name) { if (!strcmp (parc->name, archive_name)) { vfs_stamp (&vfs_extfs_ops, (vfsid) parc); goto return_success; } } result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc); if (result == -1) ERRNOR (EIO, NULL); return_success: *archive = parc; return local; }
static int fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *localname) { struct vfs_s_super *super = FH_SUPER; int n, total; char buffer[8192]; struct stat s; int was_error = 0; int h; char *quoted_name; h = open (localname, O_RDONLY); if (h == -1) ERRNOR (EIO, -1); if (fstat(h, &s)<0) { close (h); ERRNOR (EIO, -1); } /* First, try this as stor: * * ( head -c number ) | ( cat > file; cat >/dev/null ) * * If `head' is not present on the remote system, `dd' will be used. * Unfortunately, we cannot trust most non-GNU `head' implementations * even if `-c' options is supported. Therefore, we separate GNU head * (and other modern heads?) using `-q' and `-' . This causes another * implementations to fail (because of "incorrect options"). * * Fallback is: * * rest=<number> * while [ $rest -gt 0 ] * do * cnt=`expr \( $rest + 255 \) / 256` * n=`dd bs=256 count=$cnt | tee -a <target_file> | wc -c` * rest=`expr $rest - $n` * done * * `dd' was not designed for full filling of input buffers, * and does not report exact number of bytes (not blocks). * Therefore a more complex shell script is needed. * * On some systems non-GNU head writes "Usage:" error report to stdout * instead of stderr. It makes impossible the use of "head || dd" * algorithm for file appending case, therefore just "dd" is used for it. */ print_vfs_message(_("fish: store %s: sending command..."), name ); quoted_name = name_quote (name, 0); /* FIXME: File size is limited to ULONG_MAX */ if (!fh->u.fish.append) n = fish_command (me, super, WAIT_REPLY, "#STOR %lu /%s\n" "echo '### 001'\n" "file=/%s\n" "res=`exec 3>&1\n" "(\n" "head -c %lu -q - || echo DD >&3\n" ") 2>/dev/null | (\n" "cat > \"$file\"\n" "cat > /dev/null\n" ")`; [ \"$res\" = DD ] && {\n" "> \"$file\"\n" "rest=%lu\n" "while [ $rest -gt 0 ]\n" "do\n" " cnt=`expr \\( $rest + 255 \\) / 256`\n" " n=`dd bs=256 count=$cnt | tee -a \"$file\" | wc -c`\n" " rest=`expr $rest - $n`\n" "done\n" "}; echo '### 200'\n", (unsigned long) s.st_size, name, quoted_name, (unsigned long) s.st_size, (unsigned long) s.st_size); else n = fish_command (me, super, WAIT_REPLY, "#STOR %lu /%s\n" "echo '### 001'\n" "{\n" "file=/%s\n" "rest=%lu\n" "while [ $rest -gt 0 ]\n" "do\n" " cnt=`expr \\( $rest + 255 \\) / 256`\n" " n=`dd bs=256 count=$cnt | tee -a \"$file\" | wc -c`\n" " rest=`expr $rest - $n`\n" "done\n" "}; echo '### 200'\n", (unsigned long) s.st_size, name, quoted_name, (unsigned long) s.st_size); g_free (quoted_name); if (n != PRELIM) { close (h); ERRNOR(E_REMOTE, -1); } total = 0; while (1) { int t; while ((n = read(h, buffer, sizeof(buffer))) < 0) { if ((errno == EINTR) && got_interrupt()) continue; print_vfs_message(_("fish: Local read failed, sending zeros") ); close(h); h = open( "/dev/zero", O_RDONLY ); } if (n == 0) break; if ((t = write (SUP.sockw, buffer, n)) != n) { if (t == -1) { me->verrno = errno; } else { me->verrno = EIO; } goto error_return; } disable_interrupt_key(); total += n; print_vfs_message(_("fish: storing %s %d (%lu)"), was_error ? _("zeros") : _("file"), total, (unsigned long) s.st_size); } close(h); if ((fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) || was_error) ERRNOR (E_REMOTE, -1); return 0; error_return: close(h); fish_get_reply(me, SUP.sockr, NULL, 0); return -1; }
static int fish_open_archive_int (struct vfs_class *me, struct vfs_s_super *super) { { const char *argv[10]; const int port_num = SUP.flags & 0xFFFF; const int sh_flags = SUP.flags & ~0xFFFF; const char *xsh = (sh_flags == FISH_FLAG_RSH ? "rsh" : "ssh"); char buf[8]; int i = 0; argv[i++] = xsh; if (sh_flags == FISH_FLAG_COMPRESSED) argv[i++] = "-C"; if (port_num) { sprintf(buf, "%d", port_num); argv[i++] = "-p"; argv[i++] = buf; } argv[i++] = "-l"; argv[i++] = SUP.user; argv[i++] = SUP.host; argv[i++] = "echo FISH:; /bin/sh"; argv[i++] = NULL; fish_pipeopen (super, xsh, argv); } { char answer[2048]; print_vfs_message (_("fish: Waiting for initial line...")); if (!vfs_s_get_line (me, SUP.sockr, answer, sizeof (answer), ':')) ERRNOR (E_PROTO, -1); print_vfs_message ("%s", answer); if (strstr (answer, "assword")) { /* Currently, this does not work. ssh reads passwords from /dev/tty, not from stdin :-(. */ message (1, MSG_ERROR, _ ("Sorry, we cannot do password authenticated connections for now.")); ERRNOR (EPERM, -1); if (!SUP.password) { char *p, *op; p = g_strconcat (_(" fish: Password required for "), SUP.user, " ", (char *) NULL); op = vfs_get_password (p); g_free (p); if (op == NULL) ERRNOR (EPERM, -1); SUP.password = op; } print_vfs_message (_("fish: Sending password...")); write (SUP.sockw, SUP.password, strlen (SUP.password)); write (SUP.sockw, "\n", 1); } } print_vfs_message (_("fish: Sending initial line...")); /* * Run `start_fish_server'. If it doesn't exist - no problem, * we'll talk directly to the shell. */ if (fish_command (me, super, WAIT_REPLY, "#FISH\necho; start_fish_server 2>&1; echo '### 200'\n") != COMPLETE) ERRNOR (E_PROTO, -1); print_vfs_message (_("fish: Handshaking version...")); if (fish_command (me, super, WAIT_REPLY, "#VER 0.0.0\necho '### 000'\n") != COMPLETE) ERRNOR (E_PROTO, -1); /* Set up remote locale to C, otherwise dates cannot be recognized */ if (fish_command (me, super, WAIT_REPLY, "LANG=C; LC_ALL=C; LC_TIME=C\n" "export LANG; export LC_ALL; export LC_TIME\n" "echo '### 200'\n") != COMPLETE) ERRNOR (E_PROTO, -1); print_vfs_message (_("fish: Setting up current directory...")); SUP.cwdir = fish_getcwd (me, super); print_vfs_message (_("fish: Connected, home %s."), SUP.cwdir); #if 0 super->name = g_strconcat ("/#sh:", SUP.user, "@", SUP.host, "/", (char *) NULL); #endif super->name = g_strdup (PATH_SEP_STR); super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755)); return 0; }
/* The returned directory should always contain a trailing slash */ static char *fish_getcwd(struct vfs_class *me, struct vfs_s_super *super) { if (fish_command (me, super, WANT_STRING, "#PWD\npwd; echo '### 200'\n") == COMPLETE) return g_strconcat (reply_str, "/", (char *) NULL); ERRNOR (EIO, NULL); }
static int tar_fh_open (vfs *me, vfs_s_fh *fh, int flags, int mode) { if ((flags & O_ACCMODE) != O_RDONLY) ERRNOR (EROFS, -1); return 0; }