From 38d6274864ebb4d31333946379496d67745cd3c5 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Wed, 28 Apr 2010 18:55:20 -0400 Subject: s3-smbd: Added code to print via spoolss. --- source3/printing/printspoolss.c | 331 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 source3/printing/printspoolss.c (limited to 'source3/printing/printspoolss.c') diff --git a/source3/printing/printspoolss.c b/source3/printing/printspoolss.c new file mode 100644 index 0000000000..aaec8918ee --- /dev/null +++ b/source3/printing/printspoolss.c @@ -0,0 +1,331 @@ +/* + Unix SMB/CIFS implementation. + Printing routines that bridge to spoolss + Copyright (C) Simo Sorce 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "printing.h" +#include "../librpc/gen_ndr/cli_spoolss.h" + +void print_spool_terminate(struct connection_struct *conn, + struct print_file_data *print_file); + +/*************************************************************************** + * Open a Document over spoolss + ***************************************************************************/ + +#define DOCNAME_DEFAULT "Remote Downlevel Document" +#ifndef PRINT_SPOOL_PREFIX +#define PRINT_SPOOL_PREFIX "smbprn." +#endif + +NTSTATUS print_spool_open(files_struct *fsp, + const char *fname, + uint16_t current_vuid) +{ + NTSTATUS status; + TALLOC_CTX *tmp_ctx; + struct print_file_data *pf; + struct rpc_pipe_client *cli; + struct spoolss_DevmodeContainer devmode_ctr; + union spoolss_DocumentInfo info; + int fd = -1; + WERROR werr; + + tmp_ctx = talloc_new(fsp); + if (!tmp_ctx) { + return NT_STATUS_NO_MEMORY; + } + + pf = talloc_zero(fsp, struct print_file_data); + if (!pf) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + pf->svcname = talloc_strdup(pf, lp_servicename(SNUM(fsp->conn))); + + /* the document name is derived from the file name. + * "Remote Downlevel Document" is added in front to + * mimic what windows does in this case */ + pf->docname = talloc_strdup(pf, DOCNAME_DEFAULT); + if (!pf->docname) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + if (fname) { + const char *p = strrchr(fname, '/'); + if (!p) { + p = fname; + } + pf->docname = talloc_asprintf_append(pf->docname, " %s", p); + if (!pf->docname) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + } + + /* Ok, now we have to open an actual file. + * Here is the reason: + * We want to write the spool job to this file in + * smbd for scalability reason (and also because + * apparently window printer drivers can seek when + * spooling to a file). + * So we first create a file, and then we pass it + * to spoolss in output_file so it can monitor and + * take over once we call EndDocPrinter(). + * Of course we will not start writing until + * StartDocPrinter() actually gives the ok. */ + + pf->filename = talloc_asprintf(pf, "%s/%s.XXXXXX", + lp_pathname(SNUM(fsp->conn)), + PRINT_SPOOL_PREFIX); + if (!pf->filename) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + errno = 0; + fd = mkstemp(pf->filename); + if (fd == -1) { + if (errno == EACCES) { + /* Common setup error, force a report. */ + DEBUG(0, ("Insufficient permissions " + "to open spool file %s.\n", + pf->filename)); + } else { + /* Normal case, report at level 3 and above. */ + DEBUG(3, ("can't open spool file %s,\n", + pf->filename)); + DEBUGADD(3, ("errno = %d (%s).\n", + errno, strerror(errno))); + } + status = map_nt_error_from_unix(errno); + goto done; + } + + /* now open a document over spoolss so that it does + * all printer verification, and eventually assigns + * a job id */ + + status = rpc_connect_spoolss_pipe(fsp->conn, &cli); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + ZERO_STRUCT(devmode_ctr); + + status = rpccli_spoolss_OpenPrinter(cli, pf, pf->svcname, + "RAW", devmode_ctr, + SEC_FLAG_MAXIMUM_ALLOWED, + &pf->handle, &werr); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + goto done; + } + + info.info1 = talloc(tmp_ctx, struct spoolss_DocumentInfo1); + if (!info.info1) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + info.info1->document_name = pf->docname; + info.info1->output_file = pf->filename; + info.info1->datatype = "RAW"; + + status = rpccli_spoolss_StartDocPrinter(cli, tmp_ctx, &pf->handle, + 1, info, &pf->jobid, &werr); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + goto done; + } + + /* Convert to RAP id. */ + pf->rap_jobid = pjobid_to_rap(pf->svcname, pf->jobid); + if (pf->rap_jobid == 0) { + /* No errno around here */ + status = NT_STATUS_ACCESS_DENIED; + goto done; + } + + /* setup a full fsp */ + status = create_synthetic_smb_fname(fsp, pf->filename, NULL, + NULL, &fsp->fsp_name); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + if (sys_fstat(fd, &fsp->fsp_name->st, false) != 0) { + status = map_nt_error_from_unix(errno); + goto done; + } + + fsp->file_id = vfs_file_id_from_sbuf(fsp->conn, &fsp->fsp_name->st); + fsp->mode = fsp->fsp_name->st.st_ex_mode; + fsp->fh->fd = fd; + + fsp->vuid = current_vuid; + fsp->can_lock = false; + fsp->can_read = false; + fsp->access_mask = FILE_GENERIC_WRITE; + fsp->can_write = true; + fsp->modified = false; + fsp->oplock_type = NO_OPLOCK; + fsp->sent_oplock_break = NO_BREAK_SENT; + fsp->is_directory = false; + + fsp->print_file = pf; + + status = NT_STATUS_OK; +done: + if (!NT_STATUS_IS_OK(status)) { + if (fd != -1) { + close(fd); + unlink(fsp->print_file->filename); + } + /* We need to delete the job from spoolss too */ + if (pf->jobid) { + print_spool_terminate(fsp->conn, pf); + } + } + talloc_free(tmp_ctx); + return status; +} + +int print_spool_write(files_struct *fsp, const char *data, uint32_t size, + SMB_OFF_T offset, uint32_t *written) +{ + SMB_STRUCT_STAT st; + ssize_t n; + int ret; + + *written = 0; + + /* first of all stat file to find out if it is still there. + * spoolss may have deleted it to signal someone has killed + * the job through it's interface */ + + if (sys_fstat(fsp->fh->fd, &st, false) != 0) { + ret = errno; + DEBUG(3, ("printfile_offset: sys_fstat failed on %s (%s)\n", + fsp_str_dbg(fsp), strerror(ret))); + return ret; + } + + /* check if the file is unlinked, this will signal spoolss has + * killed it, just return an error and close the file */ + if (st.st_ex_nlink == 0) { + close(fsp->fh->fd); + return EBADF; + } + + /* When print files go beyond 4GB, the 32-bit offset sent in + * old SMBwrite calls is relative to the current 4GB chunk + * we're writing to. + * Discovered by Sebastian Kloska . + */ + if (offset < 0xffffffff00000000LL) { + offset = (st.st_ex_size & 0xffffffff00000000LL) + offset; + } + + n = write_data_at_offset(fsp->fh->fd, data, size, offset); + if (n == -1) { + ret = errno; + print_spool_terminate(fsp->conn, fsp->print_file); + } else { + *written = n; + ret = 0; + } + + return ret; +} + +void print_spool_end(files_struct *fsp, enum file_close_type close_type) +{ + struct rpc_pipe_client *cli; + NTSTATUS status; + WERROR werr; + + status = rpc_connect_spoolss_pipe(fsp->conn, &cli); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("print_spool_end: " + "Failed to get spoolss pipe [%s]\n", + nt_errstr(status))); + return; + } + + switch (close_type) { + case NORMAL_CLOSE: + case SHUTDOWN_CLOSE: + status = rpccli_spoolss_ClosePrinter(cli, fsp->print_file, + &fsp->print_file->handle, + &werr); + if (!NT_STATUS_IS_OK(status) || + !NT_STATUS_IS_OK(status = werror_to_ntstatus(werr))) { + DEBUG(3, ("Failed to close printer %s [%s]\n", + fsp->print_file->svcname, nt_errstr(status))); + } + break; + case ERROR_CLOSE: + print_spool_terminate(fsp->conn, fsp->print_file); + break; + } +} + + +void print_spool_terminate(struct connection_struct *conn, + struct print_file_data *print_file) +{ + struct rpc_pipe_client *cli; + NTSTATUS status; + WERROR werr; + + rap_jobid_delete(print_file->svcname, print_file->jobid); + + status = rpc_connect_spoolss_pipe(conn, &cli); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("print_spool_terminate: " + "Failed to get spoolss pipe [%s]\n", + nt_errstr(status))); + return; + } + + status = rpccli_spoolss_SetJob(cli, print_file, + &print_file->handle, + print_file->jobid, + NULL, SPOOLSS_JOB_CONTROL_DELETE, + &werr); + if (!NT_STATUS_IS_OK(status) || + !NT_STATUS_IS_OK(status = werror_to_ntstatus(werr))) { + DEBUG(3, ("Failed to delete job %d [%s]\n", + print_file->jobid, nt_errstr(status))); + return; + } + status = rpccli_spoolss_ClosePrinter(cli, print_file, + &print_file->handle, + &werr); + if (!NT_STATUS_IS_OK(status) || + !NT_STATUS_IS_OK(status = werror_to_ntstatus(werr))) { + DEBUG(3, ("Failed to close printer %s [%s]\n", + print_file->svcname, nt_errstr(status))); + return; + } +} -- cgit