/* * smb_receive * fs points to the correct segment */ static int smb_receive(struct smb_sb_info *server) { struct socket *socket = server_sock(server); unsigned char * packet = server->packet; int len, result; unsigned char peek_buf[4]; result = smb_get_length(socket, peek_buf); if (result < 0) goto out; len = result; /* * Some servers do not respect our max_xmit and send * larger packets. Try to allocate a new packet, * but don't free the old one unless we succeed. */ if (len + 4 > server->packet_size) { int new_len = smb_round_length(len + 4); result = -ENOMEM; packet = smb_vmalloc(new_len); if (packet == NULL) goto out; smb_vfree(server->packet); server->packet = packet; server->packet_size = new_len; } memcpy(packet, peek_buf, 4); result = smb_receive_raw(socket, packet + 4, len); if (result < 0) { #ifdef SMBFS_DEBUG_VERBOSE printk("smb_receive: receive error: %d\n", result); #endif goto out; } server->rcls = *(packet + smb_rcls); server->err = WVAL(packet, smb_err); #ifdef SMBFS_DEBUG_VERBOSE if (server->rcls != 0) printk("smb_receive: rcls=%d, err=%d\n", server->rcls, server->err); #endif out: return result; }
static int smb_readdir(struct inode *dir, struct file *filp, void *dirent, filldir_t filldir) { int result, i = 0; struct smb_dirent *entry = NULL; struct smb_server *server = SMB_SERVER(dir); DPRINTK("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos); DDPRINTK("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n", dir->i_ino, c_ino); if ((dir == NULL) || !S_ISDIR(dir->i_mode)) { printk("smb_readdir: dir is NULL or not a directory\n"); return -EBADF; } if (c_entry == NULL) { i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE; c_entry = (struct smb_dirent *) smb_vmalloc(i); if (c_entry == NULL) { printk("smb_readdir: no MEMORY for cache\n"); return -ENOMEM; } } if (filp->f_pos == 0) { c_ino = 0; c_dev = 0; c_seen_eof = 0; if (filldir(dirent, ".", 1, filp->f_pos, smb_info_ino(SMB_INOP(dir))) < 0) { return 0; } filp->f_pos += 1; } if (filp->f_pos == 1) { if (filldir(dirent, "..", 2, filp->f_pos, smb_info_ino(SMB_INOP(dir)->dir)) < 0) { return 0; } filp->f_pos += 1; } entry = smb_search_in_cache(dir, filp->f_pos); if (entry == NULL) { if (c_seen_eof) { /* End of directory */ return 0; } result = smb_refill_dir_cache(server, dir, filp->f_pos); if (result <= 0) { return result; } entry = c_entry; } while (entry < &(c_entry[c_size])) { /* We found it. For getwd(), we have to return the correct inode in d_ino if the inode is currently in use. Otherwise the inode number does not matter. (You can argue a lot about this..) */ struct smb_inode_info *ino_info = smb_find_dir_inode(dir, entry->name, entry->len); ino_t ino = entry->f_ino; if (ino_info != NULL) { ino = smb_info_ino(ino_info); } DDPRINTK("smb_readdir: entry->name = %s\n", entry->name); DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos); if (filldir(dirent, entry->name, strlen(entry->name), entry->f_pos, ino) < 0) { break; } if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino) || (entry->f_pos != filp->f_pos)) { /* Someone has destroyed the cache while we slept in filldir */ break; } filp->f_pos += 1; entry += 1; } return 0; }
/* * 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; }