sftp-file-type.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /* sftp-file-type.c -- SFTP file type.
  2. *
  3. * Copyright (C) 2015, 2016 Artyom V. Poptsov <poptsov.artyom@gmail.com>
  4. * Copyright (C) 2017 Ludovic Courtès <ludo@gnu.org>
  5. *
  6. * This file is part of Guile-SSH.
  7. *
  8. * Guile-SSH is free software: you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License as
  10. * published by the Free Software Foundation, either version 3 of the
  11. * License, or (at your option) any later version.
  12. *
  13. * Guile-SSH is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with Guile-SSH. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <config.h>
  22. #include <libguile.h>
  23. #include <libssh/libssh.h>
  24. #include "common.h"
  25. #include "error.h"
  26. #include "sftp-session-type.h"
  27. #include "sftp-file-type.h"
  28. /* The SFTP file port type. Guile 2.2 introduced a new port API, so we have a
  29. separate implementation for these newer versions. */
  30. #if USING_GUILE_BEFORE_2_2
  31. static scm_t_bits sftp_file_tag; /* Smob tag. */
  32. #else
  33. static scm_t_port_type *sftp_file_tag;
  34. #endif
  35. enum {
  36. DEFAULT_PORT_R_BUFSZ = 256, /* Default read buffer size */
  37. DEFAULT_PORT_W_BUFSZ = 1 /* Default write buffer size */
  38. };
  39. /* Ptob callbacks. */
  40. #if USING_GUILE_BEFORE_2_2
  41. /* Read data from the channel. Return EOF if no data is available or
  42. throw `guile-ssh-error' if an error occured. */
  43. static int
  44. ptob_fill_input (SCM file)
  45. #define FUNC_NAME "ptob_fill_input"
  46. {
  47. struct sftp_file_data *fd = _scm_to_sftp_file_data (file);
  48. scm_port *pt = SCM_PTAB_ENTRY (file);
  49. ssize_t res;
  50. res = sftp_read (fd->file, pt->read_buf, pt->read_buf_size);
  51. if (! res)
  52. return EOF;
  53. else if (res < 0)
  54. guile_ssh_error1 (FUNC_NAME, "Error reading the file", file);
  55. pt->read_pos = pt->read_buf;
  56. pt->read_end = pt->read_buf + res;
  57. return *pt->read_buf;
  58. }
  59. #undef FUNC_NAME
  60. static void
  61. ptob_write (SCM file, const void* data, size_t sz)
  62. #define FUNC_NAME "ptob_write"
  63. {
  64. struct sftp_file_data *fd = _scm_to_sftp_file_data (file);
  65. ssize_t nwritten = sftp_write (fd->file, data, sz);
  66. if (nwritten != sz)
  67. guile_ssh_error1 (FUNC_NAME, "Error writing the file", file);
  68. }
  69. #undef FUNC_NAME
  70. #else /* !USING_GUILE_BEFORE_2_2 */
  71. static size_t
  72. read_from_sftp_file_port (SCM file, SCM dst, size_t start, size_t count)
  73. #define FUNC_NAME "read_from_sftp_file_port"
  74. {
  75. char *data = (char *) SCM_BYTEVECTOR_CONTENTS (dst) + start;
  76. struct sftp_file_data *fd = _scm_to_sftp_file_data (file);
  77. ssize_t res;
  78. res = sftp_read (fd->file, data, count);
  79. if (res < 0)
  80. guile_ssh_error1 (FUNC_NAME, "Error reading the file", file);
  81. return res;
  82. }
  83. #undef FUNC_NAME
  84. static size_t
  85. write_to_sftp_file_port (SCM file, SCM src, size_t start, size_t count)
  86. #define FUNC_NAME "write_to_sftp_file_port"
  87. {
  88. char *data = (char *) SCM_BYTEVECTOR_CONTENTS (src) + start;
  89. struct sftp_file_data *fd = _scm_to_sftp_file_data (file);
  90. ssize_t nwritten = sftp_write (fd->file, data, count);
  91. if (nwritten < 0)
  92. guile_ssh_error1 (FUNC_NAME, "Error reading the file", file);
  93. return nwritten;
  94. }
  95. #undef FUNC_NAME
  96. #endif /* !USING_GUILE_BEFORE_2_2 */
  97. static int
  98. ptob_input_waiting (SCM file)
  99. #define FUNC_NAME "ptob_input_waiting"
  100. {
  101. struct sftp_file_data *fd = _scm_to_sftp_file_data (file);
  102. sftp_attributes attr = sftp_fstat (fd->file);
  103. uint64_t pos = sftp_tell64 (fd->file);
  104. return attr->size - pos;
  105. }
  106. #undef FUNC_NAME
  107. #if USING_GUILE_BEFORE_2_2
  108. static SCM
  109. equalp_sftp_file (SCM x1, SCM x2)
  110. {
  111. struct sftp_file_data *fd1 = _scm_to_sftp_file_data (x1);
  112. struct sftp_file_data *fd2 = _scm_to_sftp_file_data (x2);
  113. if ((! fd1) || (! fd2))
  114. return SCM_BOOL_F;
  115. else if (fd1 != fd2)
  116. return SCM_BOOL_F;
  117. else
  118. return SCM_BOOL_T;
  119. }
  120. #endif
  121. static int
  122. print_sftp_file (SCM sftp_file, SCM port, scm_print_state *pstate)
  123. {
  124. struct sftp_file_data *fd = _scm_to_sftp_file_data (sftp_file);
  125. ssh_session session = fd->file->sftp->session;
  126. char *user = NULL;
  127. char *host = NULL;
  128. unsigned int ssh_port;
  129. int res;
  130. scm_puts ("#<sftp-file ", port);
  131. res = ssh_options_get (session, SSH_OPTIONS_USER, &user);
  132. scm_display ((res == SSH_OK) ? scm_from_locale_string (user) : SCM_UNDEFINED,
  133. port);
  134. ssh_string_free_char (user);
  135. scm_putc ('@', port);
  136. res = ssh_options_get (session, SSH_OPTIONS_HOST, &host);
  137. scm_display ((res == SSH_OK) ? scm_from_locale_string (host) : SCM_UNDEFINED,
  138. port);
  139. ssh_string_free_char (host);
  140. scm_putc (':', port);
  141. res = ssh_options_get_port (session, &ssh_port);
  142. scm_display ((res == SSH_OK) ? scm_from_int (ssh_port) : SCM_UNDEFINED,
  143. port);
  144. scm_putc (' ', port);
  145. scm_display (scm_port_filename (sftp_file), port);
  146. scm_putc (' ', port);
  147. scm_display (_scm_object_hex_address (sftp_file), port);
  148. scm_puts (">", port);
  149. return 1;
  150. }
  151. #if USING_GUILE_BEFORE_2_2
  152. /* Complete the processing of buffered output data. Currently this callback
  153. makes no effect because a SFTP_FILE uses unbuffered output. */
  154. static void
  155. ptob_flush (SCM sftp_file)
  156. #define FUNC_NAME "ptob_flush"
  157. {
  158. scm_port *pt = SCM_PTAB_ENTRY (sftp_file);
  159. size_t wrsize = pt->write_pos - pt->write_buf;
  160. if (wrsize)
  161. ptob_write (sftp_file, pt->write_buf, wrsize);
  162. pt->write_pos = pt->write_buf;
  163. }
  164. #undef FUNC_NAME
  165. #endif
  166. #if USING_GUILE_BEFORE_2_2
  167. static int
  168. #else
  169. static void
  170. #endif
  171. ptob_close (SCM sftp_file)
  172. {
  173. struct sftp_file_data *fd = _scm_to_sftp_file_data (sftp_file);
  174. #if USING_GUILE_BEFORE_2_2
  175. scm_port *pt = SCM_PTAB_ENTRY (sftp_file);
  176. ptob_flush (sftp_file);
  177. #endif
  178. if (fd)
  179. {
  180. sftp_close (fd->file);
  181. }
  182. #if USING_GUILE_BEFORE_2_2
  183. scm_gc_free (pt->write_buf, pt->write_buf_size, "port write buffer");
  184. scm_gc_free (pt->read_buf, pt->read_buf_size, "port read buffer");
  185. SCM_SETSTREAM (sftp_file, NULL);
  186. return 1;
  187. #endif
  188. }
  189. static scm_t_off
  190. ptob_seek (SCM port, scm_t_off offset, int whence)
  191. #define FUNC_NAME "ptob_seek"
  192. {
  193. struct sftp_file_data *fd = _scm_to_sftp_file_data (port);
  194. scm_t_off target;
  195. /* In Guile 2.2, PORT is flushed before this function is called; in 2.0 that
  196. wasn't the case. */
  197. #if USING_GUILE_BEFORE_2_2
  198. {
  199. scm_t_port *pt = SCM_PTAB_ENTRY (port);
  200. if (pt->rw_active == SCM_PORT_WRITE)
  201. ptob_flush (port);
  202. if (pt->rw_active == SCM_PORT_READ)
  203. scm_end_input (port);
  204. }
  205. #endif
  206. switch (whence)
  207. {
  208. case SEEK_CUR:
  209. {
  210. uint64_t current_pos = sftp_tell64 (fd->file);
  211. target = current_pos + offset;
  212. }
  213. break;
  214. case SEEK_END:
  215. {
  216. sftp_attributes attr = sftp_fstat (fd->file);
  217. if (! attr)
  218. {
  219. guile_ssh_error1 (FUNC_NAME,
  220. "Could not get file attributes",
  221. port);
  222. }
  223. target = attr->size - offset;
  224. }
  225. break;
  226. default: /* SEEK_SET */
  227. target = offset;
  228. break;
  229. }
  230. if (target < 0)
  231. scm_misc_error (FUNC_NAME, "negative offset", SCM_EOL);
  232. if (sftp_seek64 (fd->file, target))
  233. guile_ssh_error1 (FUNC_NAME, "Could not seek a file", port);
  234. return target;
  235. }
  236. #undef FUNC_NAME
  237. /* Public Scheme procedures */
  238. SCM_GSSH_DEFINE (gssh_sftp_open, "%gssh-sftp-open", 4,
  239. (SCM sftp_session, SCM path, SCM access_type, SCM mode))
  240. #define FUNC_NAME s_gssh_sftp_open
  241. {
  242. struct sftp_session_data *sftp_sd = _scm_to_sftp_session_data (sftp_session);
  243. sftp_file file;
  244. char* c_path;
  245. int c_access_type;
  246. mode_t c_mode;
  247. SCM_ASSERT (scm_is_string (path), path, SCM_ARG2, FUNC_NAME);
  248. SCM_ASSERT (scm_is_number (access_type), access_type, SCM_ARG3, FUNC_NAME);
  249. SCM_ASSERT (scm_is_number (mode), mode, SCM_ARG4, FUNC_NAME);
  250. scm_dynwind_begin (0);
  251. c_path = scm_to_locale_string (path);
  252. scm_dynwind_free (c_path);
  253. c_access_type = scm_to_uint (access_type);
  254. c_mode = scm_to_uint (mode);
  255. file = sftp_open (sftp_sd->sftp_session, c_path, c_access_type, c_mode);
  256. if (file == NULL)
  257. {
  258. guile_ssh_error1 (FUNC_NAME, "Could not open a file",
  259. scm_list_4 (sftp_session, path, access_type, mode));
  260. }
  261. scm_dynwind_end ();
  262. return _scm_from_sftp_file (file, path, sftp_session);
  263. }
  264. #undef FUNC_NAME
  265. SCM_GSSH_DEFINE (gssh_sftp_file_p, "%gssh-sftp-file?", 1, (SCM x))
  266. {
  267. #if USING_GUILE_BEFORE_2_2
  268. return scm_from_bool (SCM_SMOB_PREDICATE (sftp_file_tag, x));
  269. #else
  270. return scm_from_bool (SCM_PORTP (x)
  271. && SCM_PORT_TYPE (x) == sftp_file_tag);
  272. #endif
  273. }
  274. /* Public C procedures */
  275. /* Convert SCM object X to a SFTP file data object. */
  276. struct sftp_file_data *
  277. _scm_to_sftp_file_data (SCM x)
  278. {
  279. #if USING_GUILE_BEFORE_2_2
  280. scm_assert_smob_type (sftp_file_tag, x);
  281. #else
  282. SCM_ASSERT_TYPE (SCM_PORTP (x) && SCM_PORT_TYPE (x) == sftp_file_tag,
  283. x, 1, __func__, "sftp-file-port");
  284. #endif
  285. return (struct sftp_file_data *) SCM_STREAM (x);
  286. }
  287. /* Convert SFTP file FD to a SCM object; set SFTP_SESSION as a parent of the
  288. object. */
  289. SCM
  290. _scm_from_sftp_file (const sftp_file file, const SCM name, SCM sftp_session)
  291. {
  292. SCM ptob;
  293. struct sftp_file_data *fd = scm_gc_malloc (sizeof (struct sftp_file_data),
  294. "sftp file");
  295. fd->sftp_session = sftp_session;
  296. fd->file = file;
  297. #if USING_GUILE_BEFORE_2_2
  298. {
  299. scm_port *pt;
  300. ptob = scm_new_port_table_entry (sftp_file_tag);
  301. pt = SCM_PTAB_ENTRY (ptob);
  302. /* Output init */
  303. pt->write_buf_size = DEFAULT_PORT_R_BUFSZ;
  304. pt->write_buf = scm_gc_malloc (pt->write_buf_size, "port write buffer");
  305. pt->write_pos = pt->write_end = pt->write_buf;
  306. /* Input init */
  307. pt->read_buf_size = DEFAULT_PORT_W_BUFSZ;
  308. pt->read_buf = scm_gc_malloc (pt->read_buf_size, "port read buffer");
  309. pt->read_pos = pt->read_end = pt->read_buf;
  310. pt->rw_random = 1;
  311. SCM_SET_CELL_TYPE (ptob, sftp_file_tag | SCM_RDNG | SCM_WRTNG | SCM_OPN);
  312. SCM_SETSTREAM (ptob, fd);
  313. }
  314. #else
  315. ptob = scm_c_make_port (sftp_file_tag, SCM_RDNG | SCM_WRTNG | SCM_OPN,
  316. (scm_t_bits) fd);
  317. #endif
  318. scm_set_port_filename_x (ptob, name);
  319. return ptob;
  320. }
  321. /* Ptob initialization. */
  322. void
  323. init_sftp_file_type (void)
  324. {
  325. sftp_file_tag = scm_make_port_type ("sftp-file",
  326. #if USING_GUILE_BEFORE_2_2
  327. &ptob_fill_input,
  328. &ptob_write
  329. #else
  330. read_from_sftp_file_port,
  331. write_to_sftp_file_port
  332. #endif
  333. );
  334. #if USING_GUILE_BEFORE_2_2
  335. scm_set_port_flush (sftp_file_tag, ptob_flush);
  336. scm_set_port_equalp (sftp_file_tag, equalp_sftp_file);
  337. #endif
  338. scm_set_port_close (sftp_file_tag, ptob_close);
  339. scm_set_port_input_waiting (sftp_file_tag, ptob_input_waiting);
  340. scm_set_port_print (sftp_file_tag, print_sftp_file);
  341. scm_set_port_seek (sftp_file_tag, ptob_seek);
  342. #include "sftp-file-type.x"
  343. }
  344. /* sftp-file-type.c ends here. */