/* * This is the callback when the dcache has a lookup hit. */ static int smb_lookup_validate(struct dentry * dentry, int flags) { struct inode * inode = dentry->d_inode; unsigned long age = jiffies - dentry->d_time; int valid; /* * The default validation is based on dentry age: * we believe in dentries for 5 seconds. (But each * successful server lookup renews the timestamp.) */ valid = (age <= SMBFS_MAX_AGE); #ifdef SMBFS_DEBUG_VERBOSE if (!valid) VERBOSE("%s/%s not valid, age=%lu\n", DENTRY_PATH(dentry), age); #endif if (inode) { if (is_bad_inode(inode)) { PARANOIA("%s/%s has dud inode\n", DENTRY_PATH(dentry)); valid = 0; } else if (!valid) valid = (smb_revalidate_inode(dentry) == 0); } else { /* * What should we do for negative dentries? */ } return valid; }
/* * Write to a file (through the page cache). */ static ssize_t smb_file_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct file * file = iocb->ki_filp; struct dentry * dentry = file->f_path.dentry; ssize_t result; VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), (unsigned long) iocb->ki_left, (unsigned long) pos); result = smb_revalidate_inode(dentry); if (result) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), result); goto out; } result = smb_open(dentry, SMB_O_WRONLY); if (result) goto out; if (iocb->ki_left > 0) { result = generic_file_aio_write(iocb, iov, nr_segs, pos); VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n", (long) file->f_pos, (long) dentry->d_inode->i_size, dentry->d_inode->i_mtime, dentry->d_inode->i_atime); } out: return result; }
static ssize_t smb_file_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct file * file = iocb->ki_filp; struct dentry * dentry = file->f_path.dentry; ssize_t status; VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), (unsigned long) iocb->ki_left, (unsigned long) pos); status = smb_revalidate_inode(dentry); if (status) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), status); goto out; } VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n", (long)dentry->d_inode->i_size, dentry->d_inode->i_flags, dentry->d_inode->i_atime); status = generic_file_aio_read(iocb, iov, nr_segs, pos); out: return status; }
/* * Write a page synchronously. * Offset is the data offset within the page. */ static int smb_writepage_sync(struct inode *inode, struct page *page, unsigned long pageoffset, unsigned int count) { loff_t offset; char *buffer = kmap(page) + pageoffset; struct smb_sb_info *server = server_from_inode(inode); unsigned int wsize = smb_get_wsize(server); int result, written = 0; offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset; VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n", inode->i_ino, inode->u.smbfs_i.fileid, count, offset, wsize); do { if (count < wsize) wsize = count; result = server->ops->write(inode, offset, wsize, buffer); if (result < 0) { PARANOIA("failed write, wsize=%d, result=%d\n", wsize, result); break; } /* N.B. what if result < wsize?? */ #ifdef SMBFS_PARANOIA if (result < wsize) PARANOIA("short write, wsize=%d, result=%d\n", wsize, result); #endif buffer += wsize; offset += wsize; written += wsize; count -= wsize; /* * Update the inode now rather than waiting for a refresh. */ inode->i_mtime = inode->i_atime = CURRENT_TIME; inode->u.smbfs_i.flags |= SMB_F_LOCALWRITE; if (offset > inode->i_size) inode->i_size = offset; } while (count); kunmap(page); return written ? written : result; }
/* * This routine is called when i_nlink == 0 and i_count goes to 0. * All blocking cleanup operations need to go here to avoid races. */ static void smb_delete_inode(struct inode *ino) { DEBUG1("ino=%ld\n", ino->i_ino); lock_kernel(); if (smb_close(ino)) PARANOIA("could not close inode %ld\n", ino->i_ino); unlock_kernel(); clear_inode(ino); }
/* * This routine is called when i_nlink == 0 and i_count goes to 0. * All blocking cleanup operations need to go here to avoid races. */ static void smb_evict_inode(struct inode *ino) { DEBUG1("ino=%ld\n", ino->i_ino); truncate_inode_pages(&ino->i_data, 0); end_writeback(ino); lock_kernel(); if (smb_close(ino)) PARANOIA("could not close inode %ld\n", ino->i_ino); unlock_kernel(); }
int smb_refill_dircache(struct cache_head * cachep, struct dentry *dentry) { struct inode * inode = dentry->d_inode; int result; VERBOSE("cache %s/%s, blocks=%d\n", DENTRY_PATH(dentry), cachep->pages); /* * Fill the cache, starting at position 2. */ retry: inode->u.smbfs_i.cache_valid |= SMB_F_CACHEVALID; result = smb_proc_readdir(dentry, 2, cachep); if (result < 0) { PARANOIA("readdir failed, result=%d\n", result); goto out; } /* * Check whether the cache was invalidated while * we were doing the scan ... */ if (!(inode->u.smbfs_i.cache_valid & SMB_F_CACHEVALID)) { PARANOIA("cache invalidated, retrying\n"); goto retry; } result = cachep->status; if (!result) { cachep->valid = 1; cachep->mtime = dentry->d_inode->i_mtime; } VERBOSE("cache %s/%s status=%d, entries=%d\n", DENTRY_PATH(dentry), cachep->status, cachep->entries); out: return result; }
static int smb_get_length(struct socket *socket, unsigned char *header) { int result; unsigned char peek_buf[4]; mm_segment_t fs; re_recv: fs = get_fs(); set_fs(get_ds()); result = smb_receive_raw(socket, peek_buf, 4); set_fs(fs); if (result < 0) { PARANOIA("recv error = %d\n", -result); return result; } switch (peek_buf[0]) { case 0x00: case 0x82: break; case 0x85: DEBUG1("Got SESSION KEEP ALIVE\n"); goto re_recv; default: PARANOIA("Invalid NBT packet, code=%x\n", peek_buf[0]); return -EIO; } if (header != NULL) { memcpy(header, peek_buf, 4); } /* The length in the RFC NB header is the raw data length */ return smb_len(peek_buf); }
/* * This is called to update the inode attributes after * we've made changes to a file or directory. */ static int smb_refresh_inode(struct dentry *dentry) { struct inode *inode = dentry->d_inode; int error; struct smb_fattr fattr; error = smb_proc_getattr(dentry, &fattr); if (!error) { smb_renew_times(dentry); /* * Check whether the type part of the mode changed, * and don't update the attributes if it did. * * And don't dick with the root inode */ if (inode->i_ino == 2) return error; if (S_ISLNK(inode->i_mode)) return error; /* VFS will deal with it */ if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) { smb_set_inode_attr(inode, &fattr); } else { /* * Big trouble! The inode has become a new object, * so any operations attempted on it are invalid. * * To limit damage, mark the inode as bad so that * subsequent lookup validations will fail. */ PARANOIA("%s/%s changed mode, %07o to %07o\n", DENTRY_PATH(dentry), inode->i_mode, fattr.f_mode); fattr.f_mode = inode->i_mode; /* save mode */ make_bad_inode(inode); inode->i_mode = fattr.f_mode; /* restore mode */ /* * No need to worry about unhashing the dentry: the * lookup validation will see that the inode is bad. * But we do want to invalidate the caches ... */ if (!S_ISDIR(inode->i_mode)) invalidate_remote_inode(inode); else smb_invalid_dir_cache(inode); error = -EIO; } } return error; }
/* * Called with the server locked. */ void smb_close_socket(struct smb_sb_info *server) { struct file * file = server->sock_file; if (file) { VERBOSE("closing socket %p\n", server_sock(server)); #ifdef SMBFS_PARANOIA if (server_sock(server)->sk->data_ready == smb_data_ready) PARANOIA("still catching keepalives!\n"); #endif server->sock_file = NULL; fput(file); } }
static struct socket * server_sock(struct smb_sb_info *server) { struct file *file; if (server && (file = server->sock_file)) { #ifdef SMBFS_PARANOIA if (!smb_valid_socket(file->f_dentry->d_inode)) PARANOIA("bad socket!\n"); #endif return &file->f_dentry->d_inode->u.socket_i; } return NULL; }
/* * Read a page synchronously. */ static int smb_readpage_sync(struct dentry *dentry, struct page *page) { char *buffer = kmap(page); loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; struct smb_sb_info *server = server_from_dentry(dentry); unsigned int rsize = smb_get_rsize(server); int count = PAGE_SIZE; int result; VERBOSE("file %s/%s, count=%d@%ld, rsize=%d\n", DENTRY_PATH(dentry), count, offset, rsize); result = smb_open(dentry, SMB_O_RDONLY); if (result < 0) { PARANOIA("%s/%s open failed, error=%d\n", DENTRY_PATH(dentry), result); goto io_error; } do { if (count < rsize) rsize = count; result = server->ops->read(dentry->d_inode,offset,rsize,buffer); if (result < 0) goto io_error; count -= result; offset += result; buffer += result; dentry->d_inode->i_atime = CURRENT_TIME; if (result < rsize) break; } while (count); memset(buffer, 0, count); flush_dcache_page(page); SetPageUptodate(page); result = 0; io_error: kunmap(page); UnlockPage(page); return result; }
/* setup to receive the data part of the SMB */ static int smb_setup_bcc(struct smb_request *req) { int result = 0; req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd; if (req->rq_rlen > req->rq_bufsize) { PARANOIA("Packet too large %d > %d\n", req->rq_rlen, req->rq_bufsize); return -ENOBUFS; } req->rq_iov[0].iov_base = req->rq_buffer; req->rq_iov[0].iov_len = req->rq_rlen; req->rq_iovlen = 1; return result; }
static void smb_data_callback(void* ptr) { struct data_callback* job=ptr; struct socket *socket = job->sk->socket; unsigned char peek_buf[4]; int result; mm_segment_t fs; fs = get_fs(); set_fs(get_ds()); lock_kernel(); while (1) { result = -EIO; if (job->sk->dead) { PARANOIA("sock dead!\n"); break; } result = _recvfrom(socket, (void *) peek_buf, 1, MSG_PEEK | MSG_DONTWAIT); if (result == -EAGAIN) break; if (peek_buf[0] != 0x85) break; /* got SESSION KEEP ALIVE */ result = _recvfrom(socket, (void *) peek_buf, 4, MSG_DONTWAIT); DEBUG1("got SESSION KEEPALIVE\n"); if (result == -EAGAIN) break; } unlock_kernel(); set_fs(fs); if (result != -EAGAIN) found_data(job->sk); kfree(ptr); }
static int smb_file_mmap(struct file * file, struct vm_area_struct * vma) { struct dentry * dentry = file->f_dentry; int status; VERBOSE("file %s/%s, address %lu - %lu\n", DENTRY_PATH(dentry), vma->vm_start, vma->vm_end); status = smb_revalidate_inode(dentry); if (status) { PARANOIA("%s/%s validation failed, error=%d\n", DENTRY_PATH(dentry), status); goto out; } status = generic_file_mmap(file, vma); out: return status; }
/* * This is the callback when the dcache has a lookup hit. */ static int smb_lookup_validate(struct dentry *dentry, struct nameidata *nd) { struct smb_sb_info *server; struct inode *inode; unsigned long age; int valid; if (nd->flags & LOOKUP_RCU) return -ECHILD; server = server_from_dentry(dentry); inode = dentry->d_inode; age = jiffies - dentry->d_time; /* * The default validation is based on dentry age: * we believe in dentries for a few seconds. (But each * successful server lookup renews the timestamp.) */ valid = (age <= SMB_MAX_AGE(server)); #ifdef SMBFS_DEBUG_VERBOSE if (!valid) VERBOSE("%s/%s not valid, age=%lu\n", DENTRY_PATH(dentry), age); #endif if (inode) { lock_kernel(); if (is_bad_inode(inode)) { PARANOIA("%s/%s has dud inode\n", DENTRY_PATH(dentry)); valid = 0; } else if (!valid) valid = (smb_revalidate_inode(dentry) == 0); unlock_kernel(); } else { /* * What should we do for negative dentries? */ } return valid; }
static ssize_t smb_file_sendfile(struct file *file, loff_t *ppos, size_t count, read_actor_t actor, void *target) { struct dentry *dentry = file->f_path.dentry; ssize_t status; VERBOSE("file %s/%s, pos=%Ld, count=%d\n", DENTRY_PATH(dentry), *ppos, count); status = smb_revalidate_inode(dentry); if (status) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), status); goto out; } status = generic_file_sendfile(file, ppos, count, actor, target); out: return status; }
static ssize_t smb_file_splice_read(struct file *file, loff_t *ppos, struct pipe_inode_info *pipe, size_t count, unsigned int flags) { struct dentry *dentry = file->f_path.dentry; ssize_t status; VERBOSE("file %s/%s, pos=%Ld, count=%lu\n", DENTRY_PATH(dentry), *ppos, count); status = smb_revalidate_inode(dentry); if (status) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), status); goto out; } status = generic_file_splice_read(file, ppos, pipe, count, flags); out: return status; }
/* * Get a pointer to the cache_head structure, * mapped as the page at offset 0. The page is * kept locked while we're using the cache. */ struct cache_head * smb_get_dircache(struct dentry * dentry) { struct inode * inode = dentry->d_inode; struct cache_head * cachep; VERBOSE("finding cache for %s/%s\n", DENTRY_PATH(dentry)); cachep = (struct cache_head *) get_cached_page(inode, 0, 1); if (!cachep) goto out; if (cachep->valid) { struct cache_index * index = cachep->index; struct cache_block * block; unsigned long offset; int i; cachep->valid = 0; /* * Here we only want to find existing cache blocks, * not add new ones. */ for (i = 0; i < cachep->pages; i++, index++) { #ifdef SMBFS_PARANOIA if (index->block) PARANOIA("cache %s/%s has existing block!\n", DENTRY_PATH(dentry)); #endif offset = PAGE_SIZE + (i << PAGE_SHIFT); block = (struct cache_block *) get_cached_page(inode, offset, 0); if (!block) goto out; index->block = block; } cachep->valid = 1; } out: return cachep; }
static ssize_t smb_file_read(struct file * file, char * buf, size_t count, loff_t *ppos) { struct dentry * dentry = file->f_dentry; ssize_t status; VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), (unsigned long) count, (unsigned long) *ppos); status = smb_revalidate_inode(dentry); if (status) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), status); goto out; } VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n", (long)dentry->d_inode->i_size, dentry->d_inode->i_flags, dentry->d_inode->i_atime); status = generic_file_read(file, buf, count, ppos); out: return status; }
/* * Add a new entry to the cache. This assumes that the * entries are coming in order and are added to the end. */ void smb_add_to_cache(struct cache_head * cachep, struct cache_dirent *entry, off_t fpos) { struct inode * inode = get_cache_inode(cachep); struct cache_index * index; struct cache_block * block; unsigned long page_off; unsigned int nent, offset, len = entry->len; unsigned int needed = len + sizeof(struct cache_entry); VERBOSE("cache inode %p, status %d, adding %.*s at %ld\n", inode, cachep->status, entry->len, entry->name, fpos); /* * Don't do anything if we've had an error ... */ if (cachep->status) goto out; index = &cachep->index[cachep->idx]; if (!index->block) goto get_block; /* space available? */ if (needed < index->space) { add_entry: nent = index->num_entries; index->num_entries++; index->space -= needed; offset = index->space + index->num_entries * sizeof(struct cache_entry); block = index->block; memcpy(&block->cb_data.names[offset], entry->name, len); block->cb_data.table[nent].namelen = len; block->cb_data.table[nent].offset = offset; block->cb_data.table[nent].ino = entry->ino; cachep->entries++; VERBOSE("added entry %.*s, len=%d, pos=%ld, entries=%d\n", entry->len, entry->name, len, fpos, cachep->entries); return; } /* * This block is full ... advance the index. */ cachep->idx++; if (cachep->idx > NINDEX) /* not likely */ goto out_full; index++; #ifdef SMBFS_PARANOIA if (index->block) PARANOIA("new index already has block!\n"); #endif /* * Get the next cache block */ get_block: cachep->pages++; page_off = PAGE_SIZE + (cachep->idx << PAGE_SHIFT); block = (struct cache_block *) get_cached_page(inode, page_off, 1); if (block) { index->block = block; index->space = PAGE_SIZE; VERBOSE("inode=%p, pages=%d, block at %ld\n", inode, cachep->pages, page_off); goto add_entry; } /* * On failure, just set the return status ... */ out_full: cachep->status = -ENOMEM; out: return; }
/* * This routine checks first for "fast track" processing, as most * packets won't need to be copied. Otherwise, it allocates a new * packet to hold the incoming data. * * Note that the final server packet must be the larger of the two; * server packets aren't allowed to shrink. */ static int smb_receive_trans2(struct smb_sb_info *server, int *ldata, unsigned char **data, int *lparm, unsigned char **parm) { unsigned char *inbuf, *base, *rcv_buf = NULL; unsigned int parm_disp, parm_offset, parm_count, parm_tot, parm_len = 0; unsigned int data_disp, data_offset, data_count, data_tot, data_len = 0; unsigned int total_p = 0, total_d = 0, buf_len = 0; int result; while (1) { result = smb_receive(server); if (result < 0) goto out; inbuf = server->packet; if (server->rcls != 0) { *parm = *data = inbuf; *ldata = *lparm = 0; goto out; } /* * Extract the control data from the packet. */ data_tot = WVAL(inbuf, smb_tdrcnt); parm_tot = WVAL(inbuf, smb_tprcnt); parm_disp = WVAL(inbuf, smb_prdisp); parm_offset = WVAL(inbuf, smb_proff); parm_count = WVAL(inbuf, smb_prcnt); data_disp = WVAL(inbuf, smb_drdisp); data_offset = WVAL(inbuf, smb_droff); data_count = WVAL(inbuf, smb_drcnt); base = smb_base(inbuf); /* * Assume success and increment lengths. */ parm_len += parm_count; data_len += data_count; if (!rcv_buf) { /* * Check for fast track processing ... just this packet. */ if (parm_count == parm_tot && data_count == data_tot) { VERBOSE("fast track, parm=%u %u %u, data=%u %u %u\n", parm_disp, parm_offset, parm_count, data_disp, data_offset, data_count); *parm = base + parm_offset; *data = base + data_offset; goto success; } if (parm_tot > TRANS2_MAX_TRANSFER || data_tot > TRANS2_MAX_TRANSFER) goto out_too_long; /* * Save the total parameter and data length. */ total_d = data_tot; total_p = parm_tot; buf_len = total_d + total_p; if (server->packet_size > buf_len) buf_len = server->packet_size; buf_len = smb_round_length(buf_len); rcv_buf = smb_vmalloc(buf_len); if (!rcv_buf) goto out_no_mem; *parm = rcv_buf; *data = rcv_buf + total_p; } else if (data_tot > total_d || parm_tot > total_p) goto out_data_grew; if (parm_disp + parm_count > total_p) goto out_bad_parm; if (data_disp + data_count > total_d) goto out_bad_data; memcpy(*parm + parm_disp, base + parm_offset, parm_count); memcpy(*data + data_disp, base + data_offset, data_count); PARANOIA("copied, parm=%u of %u, data=%u of %u\n", parm_len, parm_tot, data_len, data_tot); /* * Check whether we've received all of the data. Note that * we use the packet totals -- total lengths might shrink! */ if (data_len >= data_tot && parm_len >= parm_tot) break; } /* * Install the new packet. Note that it's possible, though * unlikely, that the new packet could be smaller than the * old one, in which case we just copy the data. */ inbuf = server->packet; if (buf_len >= server->packet_size) { server->packet_size = buf_len; server->packet = rcv_buf; rcv_buf = inbuf; } else { PARANOIA("copying data, old size=%d, new size=%u\n", server->packet_size, buf_len); memcpy(inbuf, rcv_buf, parm_len + data_len); } success: *ldata = data_len; *lparm = parm_len; out: if (rcv_buf) smb_vfree(rcv_buf); return result; out_no_mem: printk(KERN_ERR "smb_receive_trans2: couldn't allocate data area\n"); result = -ENOMEM; goto out; out_too_long: printk(KERN_ERR "smb_receive_trans2: data/param too long, data=%d, parm=%d\n", data_tot, parm_tot); goto out_error; out_data_grew: printk(KERN_ERR "smb_receive_trans2: data/params grew!\n"); goto out_error; out_bad_parm: printk(KERN_ERR "smb_receive_trans2: invalid parms, disp=%d, cnt=%d, tot=%d\n", parm_disp, parm_count, parm_tot); goto out_error; out_bad_data: printk(KERN_ERR "smb_receive_trans2: invalid data, disp=%d, cnt=%d, tot=%d\n", data_disp, data_count, data_tot); out_error: result = -EIO; goto out; }
/* * Called with the server locked */ int smb_request(struct smb_sb_info *server) { unsigned long flags, sigpipe; mm_segment_t fs; sigset_t old_set; int len, result; unsigned char *buffer; result = -EBADF; buffer = server->packet; if (!buffer) goto bad_no_packet; result = -EIO; if (server->state != CONN_VALID) goto bad_no_conn; if ((result = smb_dont_catch_keepalive(server)) != 0) goto bad_conn; len = smb_len(buffer) + 4; DEBUG1("len = %d cmd = 0x%X\n", len, buffer[8]); spin_lock_irqsave(¤t->sigmask_lock, flags); sigpipe = sigismember(¤t->signal, SIGPIPE); old_set = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); fs = get_fs(); set_fs(get_ds()); result = smb_send_raw(server_sock(server), (void *) buffer, len); if (result > 0) { result = smb_receive(server); } /* read/write errors are handled by errno */ spin_lock_irqsave(¤t->sigmask_lock, flags); if (result == -EPIPE && !sigpipe) sigdelset(¤t->signal, SIGPIPE); current->blocked = old_set; recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); set_fs(fs); if (result >= 0) { int result2 = smb_catch_keepalive(server); if (result2 < 0) { printk(KERN_ERR "smb_request: catch keepalive failed\n"); result = result2; } } if (result < 0) goto bad_conn; /* * Check for fatal server errors ... */ if (server->rcls) { int error = smb_errno(server); if (error == EBADSLT) { printk(KERN_ERR "smb_request: tree ID invalid\n"); result = error; goto bad_conn; } } out: DEBUG1("result = %d\n", result); return result; bad_conn: PARANOIA("result %d, setting invalid\n", result); server->state = CONN_INVALID; smb_invalidate_inodes(server); goto out; bad_no_packet: printk(KERN_ERR "smb_request: no packet!\n"); goto out; bad_no_conn: printk(KERN_ERR "smb_request: connection %d not valid!\n", server->state); goto out; }
/* * This is not really a trans2 request, we assume that you only have * one packet to send. */ int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, int ldata, unsigned char *data, int lparam, unsigned char *param, int *lrdata, unsigned char **rdata, int *lrparam, unsigned char **rparam) { sigset_t old_set; unsigned long flags, sigpipe; mm_segment_t fs; int result; DEBUG1("com=%d, ld=%d, lp=%d\n", trans2_command, ldata, lparam); /* * These are initialized in smb_request_ok, but not here?? */ server->rcls = 0; server->err = 0; result = -EIO; if (server->state != CONN_VALID) goto out; if ((result = smb_dont_catch_keepalive(server)) != 0) goto bad_conn; spin_lock_irqsave(¤t->sigmask_lock, flags); sigpipe = sigismember(¤t->signal, SIGPIPE); old_set = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); fs = get_fs(); set_fs(get_ds()); result = smb_send_trans2(server, trans2_command, ldata, data, lparam, param); if (result >= 0) { result = smb_receive_trans2(server, lrdata, rdata, lrparam, rparam); } /* read/write errors are handled by errno */ spin_lock_irqsave(¤t->sigmask_lock, flags); if (result == -EPIPE && !sigpipe) sigdelset(¤t->signal, SIGPIPE); current->blocked = old_set; recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); set_fs(fs); if (result >= 0) { int result2 = smb_catch_keepalive(server); if (result2 < 0) { result = result2; } } if (result < 0) goto bad_conn; /* * Check for fatal server errors ... */ if (server->rcls) { int error = smb_errno(server); if (error == EBADSLT) { printk(KERN_ERR "smb_request: tree ID invalid\n"); result = error; goto bad_conn; } } out: return result; bad_conn: PARANOIA("result=%d, setting invalid\n", result); server->state = CONN_INVALID; smb_invalidate_inodes(server); goto out; }
/* * Add a request and tell smbiod to process it */ int smb_add_request(struct smb_request *req) { long timeleft; struct smb_sb_info *server = req->rq_server; int result = 0; smb_setup_request(req); if (req->rq_trans2_command) { if (req->rq_buffer == NULL) { PARANOIA("trans2 attempted without response buffer!\n"); return -EIO; } result = smb_setup_trans2request(req); } if (result < 0) return result; #ifdef SMB_DEBUG_PACKET_SIZE add_xmit_stats(req); #endif /* add 'req' to the queue of requests */ if (smb_lock_server_interruptible(server)) return -EINTR; /* * Try to send the request as the process. If that fails we queue the * request and let smbiod send it later. */ /* FIXME: each server has a number on the maximum number of parallel requests. 10, 50 or so. We should not allow more requests to be active. */ if (server->mid > 0xf000) server->mid = 0; req->rq_mid = server->mid++; WSET(req->rq_header, smb_mid, req->rq_mid); result = 0; if (server->state == CONN_VALID) { if (list_empty(&server->xmitq)) result = smb_request_send_req(req); if (result < 0) { /* Connection lost? */ server->conn_error = result; server->state = CONN_INVALID; } } if (result != 1) list_add_tail(&req->rq_queue, &server->xmitq); smb_rget(req); if (server->state != CONN_VALID) smbiod_retry(server); smb_unlock_server(server); smbiod_wake_up(); timeleft = wait_event_interruptible_timeout(req->rq_wait, req->rq_flags & SMB_REQ_RECEIVED, 30*HZ); if (!timeleft || signal_pending(current)) { /* * On timeout or on interrupt we want to try and remove the * request from the recvq/xmitq. * First check if the request is still part of a queue. (May * have been removed by some error condition) */ smb_lock_server(server); if (!list_empty(&req->rq_queue)) { list_del_init(&req->rq_queue); smb_rput(req); } smb_unlock_server(server); } if (!timeleft) { PARANOIA("request [%p, mid=%d] timed out!\n", req, req->rq_mid); VERBOSE("smb_com: %02x\n", *(req->rq_header + smb_com)); VERBOSE("smb_rcls: %02x\n", *(req->rq_header + smb_rcls)); VERBOSE("smb_flg: %02x\n", *(req->rq_header + smb_flg)); VERBOSE("smb_tid: %04x\n", WVAL(req->rq_header, smb_tid)); VERBOSE("smb_pid: %04x\n", WVAL(req->rq_header, smb_pid)); VERBOSE("smb_uid: %04x\n", WVAL(req->rq_header, smb_uid)); VERBOSE("smb_mid: %04x\n", WVAL(req->rq_header, smb_mid)); VERBOSE("smb_wct: %02x\n", *(req->rq_header + smb_wct)); req->rq_rcls = ERRSRV; req->rq_err = ERRtimeout; /* Just in case it was "stuck" */ smbiod_wake_up(); } VERBOSE("woke up, rcls=%d\n", req->rq_rcls); if (req->rq_rcls != 0) req->rq_errno = smb_errno(req); if (signal_pending(current)) req->rq_errno = -ERESTARTSYS; return req->rq_errno; }