/* * 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 void ncp_delete_inode(struct inode *inode) { if (S_ISDIR(inode->i_mode)) { DDPRINTK(KERN_DEBUG "ncp_delete_inode: put directory %ld\n", inode->i_ino); ncp_invalid_dir_cache(inode); } if (NCP_FINFO(inode)->opened && ncp_make_closed(inode) != 0) { /* We can't do anything but complain. */ printk(KERN_ERR "ncp_delete_inode: could not close\n"); } clear_inode(inode); }
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 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; }
int ncp_notify_change(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; int result = 0; int info_mask; struct nw_modify_dos_info info; result = -EIO; if (!ncp_conn_valid(NCP_SERVER(inode))) goto out; result = inode_change_ok(inode, attr); if (result < 0) goto out; result = -EPERM; if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != NCP_SERVER(inode)->m.uid))) goto out; if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != NCP_SERVER(inode)->m.gid))) goto out; if (((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) goto out; info_mask = 0; memset(&info, 0, sizeof(info)); #if 1 if ((attr->ia_valid & ATTR_MODE) != 0) { if (!S_ISREG(inode->i_mode)) { return -EPERM; } else { umode_t newmode; info_mask |= DM_ATTRIBUTES; newmode=attr->ia_mode; newmode &= NCP_SERVER(inode)->m.file_mode; if (newmode & 0222) /* any write bit set */ { info.attributes &= ~0x60001; } else { info.attributes |= 0x60001; } } } #endif 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)); info.creationTime = le16_to_cpu(info.creationTime); info.creationDate = le16_to_cpu(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)); info.modifyTime = le16_to_cpu(info.modifyTime); info.modifyDate = le16_to_cpu(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)); info.lastAccessDate = le16_to_cpu(info.lastAccessDate); } if (info_mask != 0) { result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), inode, info_mask, &info); if (result != 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(KERN_DEBUG "ncpfs: trying to change size to %ld\n", 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 */ result = ncp_make_closed(inode); } ncp_invalid_dir_cache(dentry->d_parent->d_inode); out: return result; }