int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { int i,length; struct inode *inode; char *link; #ifdef DEBUG printk("ncp_symlink(dir=%p,dentry=%p,symname=%s)\n",dir,dentry,symname); #endif if (!(NCP_SERVER(dir)->m.flags & NCP_MOUNT_SYMLINKS)) return -EPERM; /* EPERM is returned by VFS if symlink procedure does not exist */ if ((length=strlen(symname))>NCP_MAX_SYMLINK_SIZE) return -EINVAL; if ((link=(char *)kmalloc(length+9,GFP_NFS))==NULL) return -ENOMEM; if (ncp_create_new(dir,dentry,0,aSHARED|aHIDDEN)) { kfree(link); return -EIO; } inode=dentry->d_inode; ((__u32 *)link)[0]=NCP_SYMLINK_MAGIC0; ((__u32 *)link)[1]=NCP_SYMLINK_MAGIC1; memcpy(link+8, symname, length+1); /* including last zero for io2vol */ /* map to/from server charset, do not touch upper/lower case as symlink can point out of ncp filesystem */ io2vol(NCP_SERVER(inode), link+8, 0); if(ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, 0, length+8, link, &i) || i!=length+8) { kfree(link); return -EIO; } kfree(link); return 0; }
int ncp_notify_change(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; int result = 0; __le32 info_mask; struct nw_modify_dos_info info; struct ncp_server *server; result = -EIO; lock_kernel(); server = NCP_SERVER(inode); if ((!server) || !ncp_conn_valid(server)) goto out; /* ageing the dentry to force validation */ ncp_age_dentry(server, dentry); result = inode_change_ok(inode, attr); if (result < 0) goto out; result = -EPERM; if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->m.uid))) goto out; if (((attr->ia_valid & ATTR_GID) && (attr->ia_gid != server->m.gid))) goto out; if (((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXUGO)))) goto out; info_mask = 0; memset(&info, 0, sizeof(info)); #if 1 if ((attr->ia_valid & ATTR_MODE) != 0) { umode_t newmode = attr->ia_mode; info_mask |= DM_ATTRIBUTES; if (S_ISDIR(inode->i_mode)) { newmode &= server->m.dir_mode; } else { #ifdef CONFIG_NCPFS_EXTRAS if (server->m.flags & NCP_MOUNT_EXTRAS) { /* any non-default execute bit set */ if (newmode & ~server->m.file_mode & S_IXUGO) info.attributes |= aSHARED | aSYSTEM; /* read for group/world and not in default file_mode */ else if (newmode & ~server->m.file_mode & S_IRUGO) info.attributes |= aSHARED; } else #endif newmode &= server->m.file_mode; } if (newmode & S_IWUGO) info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); else info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); #ifdef CONFIG_NCPFS_NFS_NS if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) { result = ncp_modify_nfs_info(server, NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum, attr->ia_mode, 0); if (result != 0) goto out; info.attributes &= ~(aSHARED | aSYSTEM); { /* mark partial success */ struct iattr tmpattr; tmpattr.ia_valid = ATTR_MODE; tmpattr.ia_mode = attr->ia_mode; result = inode_setattr(inode, &tmpattr); if (result) goto out; } } #endif } #endif /* Do SIZE before attributes, otherwise mtime together with size does not work... */ if ((attr->ia_valid & ATTR_SIZE) != 0) { int written; DPRINTK("ncpfs: trying to change size to %ld\n", attr->ia_size); if ((result = ncp_make_open(inode, O_WRONLY)) < 0) { result = -EACCES; goto out; } ncp_write_kernel(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_inode_close(inode); result = ncp_make_closed(inode); if (result) goto out; { struct iattr tmpattr; tmpattr.ia_valid = ATTR_SIZE; tmpattr.ia_size = attr->ia_size; result = inode_setattr(inode, &tmpattr); if (result) goto out; } } if ((attr->ia_valid & ATTR_CTIME) != 0) { info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE); ncp_date_unix2dos(attr->ia_ctime.tv_sec, &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.tv_sec, &info.modifyTime, &info.modifyDate); } if ((attr->ia_valid & ATTR_ATIME) != 0) { __le16 dummy; info_mask |= (DM_LAST_ACCESS_DATE); ncp_date_unix2dos(attr->ia_atime.tv_sec, &dummy, &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; } else goto out; } #ifdef CONFIG_NCPFS_STRONG if ((!result) && (info_mask & DM_ATTRIBUTES)) NCP_FINFO(inode)->nwattr = info.attributes; #endif } if (!result) result = inode_setattr(inode, attr); out: unlock_kernel(); return result; }
static ssize_t ncp_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); size_t already_written = 0; size_t bufsize; int errno; void *bouncebuffer; off_t pos; ncp_dbg(1, "enter %pD2\n", file); errno = generic_write_checks(iocb, from); if (errno <= 0) return errno; errno = ncp_make_open(inode, O_WRONLY); if (errno) { ncp_dbg(1, "open failed, error=%d\n", errno); return errno; } bufsize = NCP_SERVER(inode)->buffer_size; errno = file_update_time(file); if (errno) goto outrel; bouncebuffer = vmalloc(bufsize); if (!bouncebuffer) { errno = -EIO; /* -ENOMEM */ goto outrel; } pos = iocb->ki_pos; while (iov_iter_count(from)) { int written_this_time; size_t to_write = min_t(size_t, bufsize - (pos % bufsize), iov_iter_count(from)); if (copy_from_iter(bouncebuffer, to_write, from) != to_write) { errno = -EFAULT; break; } if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_write, bouncebuffer, &written_this_time) != 0) { errno = -EIO; break; } pos += written_this_time; already_written += written_this_time; if (written_this_time != to_write) break; } vfree(bouncebuffer); iocb->ki_pos = pos; if (pos > i_size_read(inode)) { mutex_lock(&inode->i_mutex); if (pos > i_size_read(inode)) i_size_write(inode, pos); mutex_unlock(&inode->i_mutex); } ncp_dbg(1, "exit %pD2\n", file); outrel: ncp_inode_close(inode); return already_written ? already_written : errno; }
static ssize_t ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; size_t already_written = 0; off_t pos; size_t bufsize; int errno; void* bouncebuffer; DPRINTK("ncp_file_write: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); errno = -EIO; if (!ncp_conn_valid(NCP_SERVER(inode))) goto out; if (!S_ISREG(inode->i_mode)) { DPRINTK("ncp_file_write: write to non-file, mode %07o\n", inode->i_mode); return -EINVAL; } errno = 0; if (!count) goto out; errno = ncp_make_open(inode, O_WRONLY); if (errno) { DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); return errno; } pos = *ppos; if (file->f_flags & O_APPEND) { pos = inode->i_size; } bufsize = NCP_SERVER(inode)->buffer_size; already_written = 0; bouncebuffer = vmalloc(bufsize); if (!bouncebuffer) { errno = -EIO; /* -ENOMEM */ goto outrel; } while (already_written < count) { int written_this_time; size_t to_write = min_t(unsigned int, bufsize - (pos % bufsize), count - already_written); if (copy_from_user(bouncebuffer, buf, to_write)) { errno = -EFAULT; break; } if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_write, bouncebuffer, &written_this_time) != 0) { errno = -EIO; break; } pos += written_this_time; buf += written_this_time; already_written += written_this_time; if (written_this_time != to_write) { break; } } vfree(bouncebuffer); inode->i_mtime = inode->i_atime = CURRENT_TIME; *ppos = pos; if (pos > inode->i_size) { inode->i_size = pos; } DPRINTK("ncp_file_write: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); outrel: ncp_inode_close(inode); out: return already_written ? already_written : errno; }
static ssize_t ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; size_t already_written = 0; off_t pos; size_t bufsize; int errno; void* bouncebuffer; DPRINTK("ncp_file_write: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); if ((ssize_t) count < 0) return -EINVAL; pos = *ppos; if (file->f_flags & O_APPEND) { pos = i_size_read(inode); } if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) { if (pos >= MAX_NON_LFS) { return -EFBIG; } if (count > MAX_NON_LFS - (u32)pos) { count = MAX_NON_LFS - (u32)pos; } } if (pos >= inode->i_sb->s_maxbytes) { if (count || pos > inode->i_sb->s_maxbytes) { return -EFBIG; } } if (pos + count > inode->i_sb->s_maxbytes) { count = inode->i_sb->s_maxbytes - pos; } if (!count) return 0; errno = ncp_make_open(inode, O_WRONLY); if (errno) { DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); return errno; } bufsize = NCP_SERVER(inode)->buffer_size; already_written = 0; bouncebuffer = vmalloc(bufsize); if (!bouncebuffer) { errno = -EIO; /* -ENOMEM */ goto outrel; } while (already_written < count) { int written_this_time; size_t to_write = min_t(unsigned int, bufsize - (pos % bufsize), count - already_written); if (copy_from_user(bouncebuffer, buf, to_write)) { errno = -EFAULT; break; } if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_write, bouncebuffer, &written_this_time) != 0) { errno = -EIO; break; } pos += written_this_time; buf += written_this_time; already_written += written_this_time; if (written_this_time != to_write) { break; } } vfree(bouncebuffer); file_update_time(file); *ppos = pos; if (pos > i_size_read(inode)) { mutex_lock(&inode->i_mutex); if (pos > i_size_read(inode)) i_size_write(inode, pos); mutex_unlock(&inode->i_mutex); } DPRINTK("ncp_file_write: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); outrel: ncp_inode_close(inode); return already_written ? already_written : errno; }
int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct inode *inode; char *rawlink; int length, err, i, outlen; int kludge; int mode; __le32 attr; unsigned int hdr; DPRINTK("ncp_symlink(dir=%p,dentry=%p,symname=%s)\n",dir,dentry,symname); if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber)) kludge = 0; else #ifdef CONFIG_NCPFS_EXTRAS if (NCP_SERVER(dir)->m.flags & NCP_MOUNT_SYMLINKS) kludge = 1; else #endif /* EPERM is returned by VFS if symlink procedure does not exist */ return -EPERM; rawlink = kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_KERNEL); if (!rawlink) return -ENOMEM; if (kludge) { mode = 0; attr = aSHARED | aHIDDEN; ((__le32 *)rawlink)[0]=NCP_SYMLINK_MAGIC0; ((__le32 *)rawlink)[1]=NCP_SYMLINK_MAGIC1; hdr = 8; } else { mode = S_IFLNK | S_IRWXUGO; attr = 0; hdr = 0; } length = strlen(symname); /* map to/from server charset, do not touch upper/lower case as symlink can point out of ncp filesystem */ outlen = NCP_MAX_SYMLINK_SIZE - hdr; err = ncp_io2vol(NCP_SERVER(dir), rawlink + hdr, &outlen, symname, length, 0); if (err) goto failfree; outlen += hdr; err = -EIO; if (ncp_create_new(dir,dentry,mode,0,attr)) { goto failfree; } inode=dentry->d_inode; if (ncp_make_open(inode, O_WRONLY)) goto failfree; if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, 0, outlen, rawlink, &i) || i!=outlen) { goto fail; } ncp_inode_close(inode); ncp_make_closed(inode); kfree(rawlink); return 0; fail:; ncp_inode_close(inode); ncp_make_closed(inode); failfree:; kfree(rawlink); return err; }