/* * Defer release of inode_info and file_info structures until the inode * has been cleared. This avoids a race condition allowing the inode to * be put back in use before being cleared. Also, temporarily increment * i_count after clear_inode() so that the inode can't be reused. */ static void ncp_put_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct ncp_server *server = NCP_SERVER(inode); struct ncp_inode_info *iinfo = NCP_INOP(inode); struct nw_file_info *finfo = NCP_FINFO(inode); /* * This operation may block, so we lock before checking the count. */ lock_super(sb); if (inode->i_count > 1) { printk("ncp_put_inode: inode in use device %s, inode %ld, count=%ld\n", kdevname(inode->i_dev), inode->i_ino, inode->i_count); goto unlock; } DDPRINTK("ncp_put_inode: put %s\n", finfo->i.entryName); /* * This operation should never block. */ if (S_ISDIR(inode->i_mode)) { DDPRINTK("ncp_put_inode: put directory %ld\n", inode->i_ino); ncp_invalid_dir_cache(inode); } clear_inode(inode); /* * After clearing the inode i_count will be 0 in 2.0.xx kernels. * To keep the inode from being reused as free if we block while * closing the file, increment i_count temporarily. */ inode->i_count++; if (finfo->opened != 0) { if (ncp_close_file(server, finfo->file_handle) != 0) { /* We can't do anything but complain. */ printk("ncp_put_inode: could not close %s\n", finfo->i.entryName); } } ncp_free_inode_info(iinfo); inode->i_count--; unlock: unlock_super(sb); }
static int ncp_file_write(struct inode *inode, struct file *file, const char *buf, int count) { int bufsize, already_written; off_t pos; int errno; if (inode == NULL) { DPRINTK("ncp_file_write: inode = NULL\n"); return -EINVAL; } if (!ncp_conn_valid(NCP_SERVER(inode))) { return -EIO; } if (!S_ISREG(inode->i_mode)) { DPRINTK("ncp_file_write: write to non-file, mode %07o\n", inode->i_mode); return -EINVAL; } DPRINTK("ncp_file_write: enter %s\n", NCP_ISTRUCT(inode)->entryName); if (count <= 0) { return 0; } if ((errno = ncp_make_open(inode, O_RDWR)) != 0) { return errno; } pos = file->f_pos; if (file->f_flags & O_APPEND) { pos = inode->i_size; } bufsize = NCP_SERVER(inode)->buffer_size; already_written = 0; while (already_written < count) { int written_this_time; int to_write = min(bufsize - (pos % bufsize), count - already_written); if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_write, buf, &written_this_time) != 0) { return -EIO; } pos += written_this_time; buf += written_this_time; already_written += written_this_time; if (written_this_time < to_write) { break; } } inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_dirt = 1; file->f_pos = pos; if (pos > inode->i_size) { inode->i_size = pos; ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); } DPRINTK("ncp_file_write: exit %s\n", NCP_ISTRUCT(inode)->entryName); return already_written; }
static void ncp_read_inode(struct inode *inode) { /* Our task should be extremely simple here. We only have to look up the information somebody else (ncp_iget) put into the inode tree. The address of this information is the inode->i_ino. Just to make sure everything went well, we check it's there. */ struct ncp_inode_info *inode_info = ncp_find_inode(inode); if (inode_info == NULL) { /* Ok, now we're in trouble. The inode info is not there. What should we do now??? */ printk("ncp_read_inode: inode info not found\n"); return; } inode_info->state = NCP_INODE_VALID; NCP_INOP(inode) = inode_info; inode_info->inode = inode; if (NCP_ISTRUCT(inode)->attributes & aDIR) { inode->i_mode = NCP_SERVER(inode)->m.dir_mode; /* for directories dataStreamSize seems to be some Object ID ??? */ inode->i_size = 512; } else { inode->i_mode = NCP_SERVER(inode)->m.file_mode; inode->i_size = NCP_ISTRUCT(inode)->dataStreamSize; } DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode); inode->i_nlink = 1; inode->i_uid = NCP_SERVER(inode)->m.uid; inode->i_gid = NCP_SERVER(inode)->m.gid; inode->i_blksize = 512; inode->i_rdev = 0; if ((inode->i_blksize != 0) && (inode->i_size != 0)) { inode->i_blocks = (inode->i_size - 1) / inode->i_blksize + 1; } else { inode->i_blocks = 0; } inode->i_mtime = ncp_date_dos2unix(NCP_ISTRUCT(inode)->modifyTime, NCP_ISTRUCT(inode)->modifyDate); inode->i_ctime = ncp_date_dos2unix(NCP_ISTRUCT(inode)->creationTime, NCP_ISTRUCT(inode)->creationDate); inode->i_atime = ncp_date_dos2unix(0, NCP_ISTRUCT(inode)->lastAccessDate); if (S_ISREG(inode->i_mode)) { inode->i_op = &ncp_file_inode_operations; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ncp_dir_inode_operations; } else { inode->i_op = NULL; } }
static int ncp_notify_change(struct inode *inode, struct iattr *attr) { int result = 0; int info_mask; struct nw_modify_dos_info info; if (!ncp_conn_valid(NCP_SERVER(inode))) { return -EIO; } if ((result = inode_change_ok(inode, attr)) < 0) return result; if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != NCP_SERVER(inode)->m.uid))) return -EPERM; if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != NCP_SERVER(inode)->m.gid))) return -EPERM; if (((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) return -EPERM; info_mask = 0; memset(&info, 0, sizeof(info)); if ((attr->ia_valid & ATTR_CTIME) != 0) { info_mask |= (DM_CREATE_TIME|DM_CREATE_DATE); ncp_date_unix2dos(attr->ia_ctime, &(info.creationTime), &(info.creationDate)); } if ((attr->ia_valid & ATTR_MTIME) != 0) { info_mask |= (DM_MODIFY_TIME|DM_MODIFY_DATE); ncp_date_unix2dos(attr->ia_mtime, &(info.modifyTime), &(info.modifyDate)); } if ((attr->ia_valid & ATTR_ATIME) != 0) { __u16 dummy; info_mask |= (DM_LAST_ACCESS_DATE); ncp_date_unix2dos(attr->ia_ctime, &(dummy), &(info.lastAccessDate)); } if (info_mask != 0) { if ((result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), NCP_ISTRUCT(inode), info_mask, &info)) != 0) { result = -EACCES; if (info_mask == (DM_CREATE_TIME|DM_CREATE_DATE)) { /* NetWare seems not to allow this. I do not know why. So, just tell the user everything went fine. This is a terrible hack, but I do not know how to do this correctly. */ result = 0; } } } if ((attr->ia_valid & ATTR_SIZE) != 0) { int written; DPRINTK("ncpfs: trying to change size of %s to %ld\n", NCP_ISTRUCT(inode)->entryName, attr->ia_size); if ((result = ncp_make_open(inode, O_RDWR)) < 0) { return -EACCES; } ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, attr->ia_size, 0, "", &written); /* According to ndir, the changes only take effect after closing the file */ ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle); NCP_FINFO(inode)->opened = 0; result = 0; } ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); return result; }