static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev, const char *symname, unsigned int size, int excl) { const struct qstr *name = &dentry->d_name; struct gfs2_holder ghs[2]; struct inode *inode = NULL; struct gfs2_inode *dip = GFS2_I(dir); struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 }; int error; u64 generation; struct buffer_head *bh = NULL; if (!name->len || name->len > GFS2_FNAMESIZE) return -ENAMETOOLONG; error = gfs2_glock_nq_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); if (error) goto fail; error = create_ok(dip, name, mode); if ((error == -EEXIST) && S_ISREG(mode) && !excl) { inode = gfs2_lookupi(dir, &dentry->d_name, 0); gfs2_glock_dq_uninit(ghs); d_instantiate(dentry, inode); return IS_ERR(inode) ? PTR_ERR(inode) : 0; } if (error) goto fail_gunlock; error = alloc_dinode(dip, &inum.no_addr, &generation); if (error) goto fail_gunlock; inum.no_formal_ino = generation; error = gfs2_glock_nq_num(sdp, inum.no_addr, &gfs2_inode_glops, LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1); if (error) goto fail_gunlock; error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev, symname, size, &bh); if (error) goto fail_gunlock2; inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, inum.no_formal_ino, 0); if (IS_ERR(inode)) goto fail_gunlock2; error = gfs2_inode_refresh(GFS2_I(inode)); if (error) goto fail_gunlock2; error = gfs2_acl_create(dip, inode); if (error) goto fail_gunlock2; error = gfs2_security_init(dip, GFS2_I(inode), name); if (error) goto fail_gunlock2; error = link_dinode(dip, name, GFS2_I(inode)); if (error) goto fail_gunlock2; if (bh) brelse(bh); gfs2_trans_end(sdp); /* Check if we reserved space in the rgrp. Function link_dinode may not, depending on whether alloc is required. */ if (dip->i_res) gfs2_inplace_release(dip); gfs2_quota_unlock(dip); gfs2_qadata_put(dip); mark_inode_dirty(inode); gfs2_glock_dq_uninit_m(2, ghs); d_instantiate(dentry, inode); return 0; fail_gunlock2: gfs2_glock_dq_uninit(ghs + 1); fail_gunlock: gfs2_glock_dq_uninit(ghs); if (inode && !IS_ERR(inode)) { set_bit(GIF_ALLOC_FAILED, &GFS2_I(inode)->i_flags); iput(inode); } fail: if (bh) brelse(bh); return error; } /** * gfs2_create - Create a file * @dir: The directory in which to create the file * @dentry: The dentry of the new file * @mode: The mode of the new file * * Returns: errno */ static int gfs2_create(struct inode *dir, struct dentry *dentry, umode_t mode, struct nameidata *nd) { int excl = 0; if (nd && (nd->flags & LOOKUP_EXCL)) excl = 1; return gfs2_create_inode(dir, dentry, S_IFREG | mode, 0, NULL, 0, excl); }
struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, unsigned int mode, dev_t dev) { struct inode *inode = NULL; struct gfs2_inode *dip = ghs->gh_gl->gl_object; struct inode *dir = &dip->i_inode; struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 }; int error; u64 generation; struct buffer_head *bh = NULL; if (!name->len || name->len > GFS2_FNAMESIZE) return ERR_PTR(-ENAMETOOLONG); gfs2_holder_reinit(LM_ST_EXCLUSIVE, 0, ghs); error = gfs2_glock_nq(ghs); if (error) goto fail; error = create_ok(dip, name, mode); if (error) goto fail_gunlock; error = alloc_dinode(dip, &inum.no_addr, &generation); if (error) goto fail_gunlock; inum.no_formal_ino = generation; error = gfs2_glock_nq_num(sdp, inum.no_addr, &gfs2_inode_glops, LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1); if (error) goto fail_gunlock; error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev, &bh); if (error) goto fail_gunlock2; inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, inum.no_formal_ino); if (IS_ERR(inode)) goto fail_gunlock2; error = gfs2_inode_refresh(GFS2_I(inode)); if (error) goto fail_gunlock2; error = gfs2_acl_create(dip, inode); if (error) goto fail_gunlock2; error = gfs2_security_init(dip, GFS2_I(inode)); if (error) goto fail_gunlock2; error = link_dinode(dip, name, GFS2_I(inode)); if (error) goto fail_gunlock2; if (bh) brelse(bh); return inode; fail_gunlock2: gfs2_glock_dq_uninit(ghs + 1); if (inode && !IS_ERR(inode)) iput(inode); fail_gunlock: gfs2_glock_dq(ghs); fail: if (bh) brelse(bh); return ERR_PTR(error); } static int __gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) { struct inode *inode = &ip->i_inode; struct buffer_head *dibh; int error; error = gfs2_meta_inode_buffer(ip, &dibh); if (error) return error; if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size != i_size_read(inode)) { error = vmtruncate(inode, attr->ia_size); if (error) return error; } setattr_copy(inode, attr); mark_inode_dirty(inode); gfs2_assert_warn(GFS2_SB(inode), !error); gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); brelse(dibh); return 0; } /** * gfs2_setattr_simple - * @ip: * @attr: * * Called with a reference on the vnode. * * Returns: errno */ int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) { int error; if (current->journal_info) return __gfs2_setattr_simple(ip, attr); error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE, 0); if (error) return error; error = __gfs2_setattr_simple(ip, attr); gfs2_trans_end(GFS2_SB(&ip->i_inode)); return error; }
static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev, const char *symname, unsigned int size, int excl) { const struct qstr *name = &dentry->d_name; struct gfs2_holder ghs[2]; struct inode *inode = NULL; struct gfs2_inode *dip = GFS2_I(dir), *ip; struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_glock *io_gl; int error; struct buffer_head *bh = NULL; u32 aflags = 0; if (!name->len || name->len > GFS2_FNAMESIZE) return -ENAMETOOLONG; error = gfs2_rs_alloc(dip); if (error) return error; error = gfs2_glock_nq_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); if (error) goto fail; error = create_ok(dip, name, mode); if ((error == -EEXIST) && S_ISREG(mode) && !excl) { inode = gfs2_lookupi(dir, &dentry->d_name, 0); gfs2_glock_dq_uninit(ghs); d_instantiate(dentry, inode); return IS_ERR(inode) ? PTR_ERR(inode) : 0; } if (error) goto fail_gunlock; inode = new_inode(sdp->sd_vfs); if (!inode) { gfs2_glock_dq_uninit(ghs); return -ENOMEM; } ip = GFS2_I(inode); error = gfs2_rs_alloc(ip); if (error) goto fail_free_inode; set_bit(GIF_INVALID, &ip->i_flags); inode->i_mode = mode; inode->i_rdev = dev; inode->i_size = size; munge_mode_uid_gid(dip, inode); ip->i_goal = dip->i_goal; if ((GFS2_I(sdp->sd_root_dir->d_inode) == dip) || (dip->i_diskflags & GFS2_DIF_TOPDIR)) aflags |= GFS2_AF_ORLOV; error = alloc_dinode(ip, aflags); if (error) goto fail_free_inode; error = gfs2_glock_get(sdp, ip->i_no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl); if (error) goto fail_free_inode; ip->i_gl->gl_object = ip; error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1); if (error) goto fail_free_inode; error = make_dinode(dip, ip, symname, &bh); if (error) goto fail_gunlock2; error = gfs2_glock_get(sdp, ip->i_no_addr, &gfs2_iopen_glops, CREATE, &io_gl); if (error) goto fail_gunlock2; error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh); if (error) goto fail_gunlock2; ip->i_iopen_gh.gh_gl->gl_object = ip; gfs2_glock_put(io_gl); gfs2_set_iop(inode); insert_inode_hash(inode); error = gfs2_inode_refresh(ip); if (error) goto fail_gunlock3; error = gfs2_acl_create(dip, inode); if (error) goto fail_gunlock3; error = gfs2_security_init(dip, ip, name); if (error) goto fail_gunlock3; error = link_dinode(dip, name, ip); if (error) goto fail_gunlock3; if (bh) brelse(bh); gfs2_trans_end(sdp); gfs2_inplace_release(dip); gfs2_quota_unlock(dip); mark_inode_dirty(inode); gfs2_glock_dq_uninit_m(2, ghs); d_instantiate(dentry, inode); return 0; fail_gunlock3: gfs2_glock_dq_uninit(ghs + 1); if (ip->i_gl) gfs2_glock_put(ip->i_gl); goto fail_gunlock; fail_gunlock2: gfs2_glock_dq_uninit(ghs + 1); fail_free_inode: if (ip->i_gl) gfs2_glock_put(ip->i_gl); gfs2_rs_delete(ip); free_inode_nonrcu(inode); inode = NULL; fail_gunlock: gfs2_glock_dq_uninit(ghs); if (inode && !IS_ERR(inode)) { set_bit(GIF_ALLOC_FAILED, &GFS2_I(inode)->i_flags); iput(inode); } fail: if (bh) brelse(bh); return error; }
struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, unsigned int mode, dev_t dev) { struct inode *inode = NULL; struct gfs2_inode *dip = ghs->gh_gl->gl_object; struct inode *dir = &dip->i_inode; struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 }; int error; u64 generation; if (!name->len || name->len > GFS2_FNAMESIZE) return ERR_PTR(-ENAMETOOLONG); gfs2_holder_reinit(LM_ST_EXCLUSIVE, 0, ghs); error = gfs2_glock_nq(ghs); if (error) goto fail; error = create_ok(dip, name, mode); if (error) goto fail_gunlock; error = pick_formal_ino(sdp, &inum.no_formal_ino); if (error) goto fail_gunlock; error = alloc_dinode(dip, &inum.no_addr, &generation); if (error) goto fail_gunlock; error = gfs2_glock_nq_num(sdp, inum.no_addr, &gfs2_inode_glops, LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1); if (error) goto fail_gunlock; error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev); if (error) goto fail_gunlock2; inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, inum.no_formal_ino); if (IS_ERR(inode)) goto fail_gunlock2; error = gfs2_inode_refresh(GFS2_I(inode)); if (error) goto fail_gunlock2; error = gfs2_acl_create(dip, GFS2_I(inode)); if (error) goto fail_gunlock2; error = gfs2_security_init(dip, GFS2_I(inode)); if (error) goto fail_gunlock2; error = link_dinode(dip, name, GFS2_I(inode)); if (error) goto fail_gunlock2; if (!inode) return ERR_PTR(-ENOMEM); return inode; fail_gunlock2: gfs2_glock_dq_uninit(ghs + 1); if (inode) iput(inode); fail_gunlock: gfs2_glock_dq(ghs); fail: return ERR_PTR(error); } /** * gfs2_rmdiri - Remove a directory * @dip: The parent directory of the directory to be removed * @name: The name of the directory to be removed * @ip: The GFS2 inode of the directory to be removed * * Assumes Glocks on dip and ip are held * * Returns: errno */ int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name, struct gfs2_inode *ip) { struct qstr dotname; int error; if (ip->i_di.di_entries != 2) { if (gfs2_consist_inode(ip)) gfs2_dinode_print(ip); return -EIO; } error = gfs2_dir_del(dip, name); if (error) return error; error = gfs2_change_nlink(dip, -1); if (error) return error; gfs2_str2qstr(&dotname, "."); error = gfs2_dir_del(ip, &dotname); if (error) return error; gfs2_str2qstr(&dotname, ".."); error = gfs2_dir_del(ip, &dotname); if (error) return error; /* It looks odd, but it really should be done twice */ error = gfs2_change_nlink(ip, -1); if (error) return error; error = gfs2_change_nlink(ip, -1); if (error) return error; return error; } /* * gfs2_unlink_ok - check to see that a inode is still in a directory * @dip: the directory * @name: the name of the file * @ip: the inode * * Assumes that the lock on (at least) @dip is held. * * Returns: 0 if the parent/child relationship is correct, errno if it isn't */ int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, const struct gfs2_inode *ip) { int error; if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode)) return -EPERM; if ((dip->i_inode.i_mode & S_ISVTX) && dip->i_inode.i_uid != current->fsuid && ip->i_inode.i_uid != current->fsuid && !capable(CAP_FOWNER)) return -EPERM; if (IS_APPEND(&dip->i_inode)) return -EPERM; error = permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, NULL); if (error) return error; error = gfs2_dir_check(&dip->i_inode, name, ip); if (error) return error; return 0; }
int sys_fsupdate_dinode (u_int sn, int action, struct dinode *d, u_int param1, u_int param2, u_int param3) { int i; char *indirblock; dinode_t *dinode = make_dinode ((u_int) d); if (dinode == NULL) { printf ("sys_fsupdate_dinode: bad address supplied (%p, %p)\n", dinode, d); return (-E_INVAL); } StaticAssert (sizeof(dinode_t) == CFFS_DINODE_SIZE); /* printf ("sys_fsupdate_dinode: action %d, param1 %d (%x), param2 %d\n", action, param1, param1, (u_int)param2); printf ("dinodeNum %d\n", dinode->dinodeNum); */ switch (action) { case CFFS_DINODE_SETMASK: cffs_dinode_setMask (dinode, (mode_t)param1); break; case CFFS_DINODE_SETUID: cffs_dinode_setUid (dinode, (uid_t)param1); break; case CFFS_DINODE_SETGID: cffs_dinode_setGid (dinode, (gid_t)param1); break; case CFFS_DINODE_SETACCTIME: cffs_dinode_setAccTime (dinode, param1); break; case CFFS_DINODE_SETMODTIME: cffs_dinode_setModTime (dinode, param1); break; case CFFS_DINODE_SETCRETIME: cffs_dinode_setCreTime (dinode, param1); break; case CFFS_DINODE_SETLENGTH: cffs_dinode_setLength (dinode, param2); assert (dinode->length == param2); break; case CFFS_DINODE_SETTYPE: if (dinode->type == param1) { return (0); } if ((dinode->dinodeNum != 0) && ((cffs_dinode_isDir(dinode)) || (param1 == S_IFDIR))) { printf ("sys_fsupdate_dinode (%d): trying to change type to or from directory (%d -> %d)\n", action, dinode->type, param1); return (-E_INVAL); } cffs_dinode_setType (dinode, (u_int16_t)param1); break; case CFFS_DINODE_SETLINKCOUNT: if ((dinode->dinodeNum != 0) && (dinode->linkCount != param1)) { printf ("sys_fsupdate_dinode (%d): changing link count directly (%d -> %d)\n", action, dinode->linkCount, param1); //return (-E_INVAL); } cffs_dinode_setLinkCount (dinode, (nlink_t)param1); break; case CFFS_DINODE_SETOPENCOUNT: cffs_dinode_setOpenCount (dinode, param1); break; case CFFS_DINODE_SETDIRNUM: if (cffs_dinode_isDir(dinode)) { printf ("sys_fsupdate_dinode (%d): trying to change dirNum of directory directly (%d -> %d) requested\n", action, dinode->dirNum, param1); return (-E_INVAL); } cffs_dinode_setDirNum (dinode, param1); break; case CFFS_DINODE_SETDIRECT: cffs_dinode_setDirect (dinode, param1, (u_int)param2); break; case CFFS_DINODE_SETINDIRECT: cffs_dinode_setIndirect (dinode, param1, (u_int)param2); break; case CFFS_DINODE_SETGROUPSTART: cffs_dinode_setGroupstart (dinode, param1); break; case CFFS_DINODE_SETGROUPSIZE: cffs_dinode_setGroupsize (dinode, param1); break; case CFFS_DINODE_CLEARBLOCKPOINTERS: cffs_dinode_clearBlockPointers(dinode, param2); break; case CFFS_DINODE_DELAYEDFREE: if ((dinode->linkCount != (cffs_dinode_isDir(dinode))) || (dinode->openCount != 0)) { printf ("sys_fsupdate_dinode (%d): can't free dinode with links or opens (%d, %d) requested\n", action, dinode->linkCount, dinode->openCount); return (-E_INVAL); } for (i=0; i<NUM_DIRECT; i++) { if (dinode->directBlocks[i] != 0) { printf ("sys_fsupdate_dinode (%d): can't free dinode with direct blocks (%d == %d) requested\n", action, i, dinode->directBlocks[i]); return (-E_INVAL); } } for (i=0; i<NUM_INDIRECT; i++) { if (dinode->indirectBlocks[i] != 0) { printf ("sys_fsupdate_dinode (%d): can't free dinode with indirect blocks (%d == %d) requested\n", action, i, dinode->indirectBlocks[i]); return (-E_INVAL); } } /* In the real system, this wants to occur only after the block is written (unless it is not dirty) */ dinode->dinodeNum = 0; break; case CFFS_DINODE_INITINDIRBLOCK: indirblock = (char *) dinode; if ((indirblock == NULL) || ((u_int)indirblock % BLOCK_SIZE)) { printf ("sys_fsupdate_dinode (%d): bad indirblock address supplied (%p)\n", action, indirblock); return (-E_INVAL); } bzero (indirblock, BLOCK_SIZE); break; case CFFS_DINODE_INITDINODE: bzero ((char *)dinode, CFFS_DINODE_SIZE); dinode->memory_sanity = CFFS_DINODE_MEMORYSANITY; dinode->type = param1; dinode->linkCount = (cffs_dinode_isDir(dinode)) ? 2 : 1; dinode->dinodeNum = param2; dinode->dirNum = param3; break; default: printf ("sys_fsupdate_dinode: unknown action (%d) requested\n", action); return (-E_INVAL); } return (0); }
int sys_fsupdate_directory (u_int sn, int action, struct embdirent *dirent, u_int param2, u_int param3, u_int param4, u_int param5) { u_int param1 = (u_int) dirent; char *dirBlock = (char *) translate_address ((param1 - (param1 % BLOCK_SIZE)), BLOCK_SIZE); if (dirBlock == NULL) { return (-E_INVAL); } dirent = (embdirent_t *) (dirBlock + (param1 % BLOCK_SIZE)); /* GROK -- need to check for write permission on directory. The relevant */ /* inode should be provided automatically by base disk protection software */ if (!isValidDirent(dirBlock, dirent)) { printf ("sys_fsupdate_directory (%d): invalid dirent specified (%x)\n", action, param1); return (-E_INVAL); } /************************** CFFS_DIRECTORY_INITDIRBLOCK ***********************/ if (action == CFFS_DIRECTORY_INITDIRBLOCK) { int i; dinode_t *dinode; if ((u_int)dirent % BLOCK_SIZE) { printf ("sys_fsupdate_directory (%d): dirent not start of block (%p)\n", action, dirent); return (-E_INVAL); } for (i=0; i<(BLOCK_SIZE/CFFS_EMBDIR_SECTOR_SIZE); i++) { dirent->type = (char) 0; dirent->entryLen = CFFS_EMBDIR_SECTOR_SPACEFORNAMES; dirent->preventryLen = 0; dinode = (dinode_t *) ((char *)dirent + CFFS_DINODE_SIZE); dinode->dinodeNum = 0; dinode = (dinode_t *) ((char *)dirent + (2*CFFS_DINODE_SIZE)); dinode->dinodeNum = 0; dinode = (dinode_t *) ((char *)dirent + (3*CFFS_DINODE_SIZE)); dinode->dinodeNum = 0; dirent = (embdirent_t *) ((char *) dirent + CFFS_EMBDIR_SECTOR_SIZE); } /*************************** CFFS_DIRECTORY_SPLITENTRY ************************/ } else if (action == CFFS_DIRECTORY_SPLITENTRY) { embdirent_t *newDirent; int splitspace = param2; int extraspace; extraspace = (dirent->type == (char)0) ? dirent->entryLen : (dirent->entryLen - embdirentsize(dirent)); if ((extraspace < SIZEOF_EMBDIRENT_T) || (extraspace < splitspace)) { printf ("sys_fsupdate_directory (%d): not enough extra space (%d) (wanted %d)\n", action, extraspace, splitspace); return (-E_INVAL); } if (splitspace == 0) { printf ("sys_fsupdate_directory (%d): bad splitspace (%d) (extraspace %d)\n", action, splitspace, extraspace); return (-E_INVAL); } dirent->entryLen -= splitspace; newDirent = (embdirent_t *) ((u_int)dirent + dirent->entryLen); if (newDirent != dirent) { newDirent->preventryLen = dirent->entryLen; } newDirent->entryLen = splitspace; newDirent->type = 0; newDirent->nameLen = 0; newDirent->name[0] = (char) 0; if (((u_int)newDirent + newDirent->entryLen) % CFFS_EMBDIR_SECTOR_SPACEFORNAMES) { dirent = (embdirent_t *) ((char *)newDirent + newDirent->entryLen); dirent->preventryLen = splitspace; } return ((param1 - (param1 % BLOCK_SIZE)) + ((u_int)newDirent - (u_int)dirBlock)); /*************************** CFFS_DIRECTORY_SETNAME ************************/ } else if (action == CFFS_DIRECTORY_SETNAME) { char *name = (char *) trup(param2); u_int namelen = param3; dinode_t *dirDInode = make_dinode(param4); if ((dirDInode == NULL) || (dirDInode->dinodeNum == 0)) { return (-E_INVAL); } /* Note: param5 is fsdev in this case, which should be discoverable */ /* from the dinode's identity... */ if ((dirent->type != 0) && (!isNameUnique(param5, dirDInode, name, namelen))) { printf ("sys_fsupdate_directory (%d): non-unique name specified (%s, %d)\n", action, name, namelen); return (-E_INVAL); } bcopy(name, dirent->name, namelen); dirent->name[namelen] = (char) 0; dirent->nameLen = namelen; /*************************** CFFS_DIRECTORY_MERGEENTRY ************************/ } else if (action == CFFS_DIRECTORY_MERGEENTRY) { embdirent_t *next = NULL; int maxleft = CFFS_EMBDIR_SECTOR_SPACEFORNAMES - (param1 % CFFS_EMBDIR_SECTOR_SIZE); int extraspace = 0; while ((extraspace < param2) && (extraspace < maxleft)) { next = (embdirent_t *) ((char *)dirent + dirent->entryLen); if (next->type != 0) { break; } extraspace += next->entryLen; } if (extraspace != param2) { printf ("sys_fsupdate_directory (%d): mismatched extraspaces (%d != %d) (maxleft %d)\n", action, extraspace, param2, maxleft); printf ("next %p, next->entryLen %d\n", next, ((next) ? (next->entryLen) : -1)); return (-E_INVAL); } dirent->entryLen += extraspace; next = (embdirent_t *) ((char *)dirent + dirent->entryLen); if ((u_int) next % CFFS_EMBDIR_SECTOR_SPACEFORNAMES) { next->preventryLen = dirent->entryLen; } /*************************** CFFS_DIRECTORY_SHIFTENTRY ************************/ } else if (action == CFFS_DIRECTORY_SHIFTENTRY) { /********* Not currently supported! ************/ assert (0); #if 0 embdirent_t *tmp; int extraspace; if ((-param2 > (param1 % CFFS_EMBDIR_SECTOR_SIZE)) || ((param2 + (param1 % CFFS_EMBDIR_SECTOR_SIZE)) >= (CFFS_EMBDIR_SECTOR_SPACEFORNAMES - SIZEOF_EMBDIRENT_T))) { printf ("sys_fsupdate_directory (%d): sizes don't work (currOff %d, move %d)\n", action, (param1 % BLOCK_SIZE), param2); return (-E_INVAL); } if (param2 <= 0) { tmp = (embdirent_t *) ((char *)dirent - dirent->preventryLen); extraspace = (tmp->type == (char)0) ? tmp->entryLen : tmp->entryLen - embdirentsize(tmp); if (extraspace < (-param2)) { printf ("sys_fsupdate_directory (%d): not enough extra space (%d < %d)\n", action, extraspace, param2); return (-E_INVAL); } tmp->entryLen -= (-param2); dirent->preventryLen = tmp->entryLen; dirent->entryLen += (-param2); bcopy ((char *) dirent, ((char *)dirent - (-param2)), embdirentsize(dirent)); tmp = (embdirent_t *) ((char *)dirent + dirent->entryLen); if ((u_int) tmp % CFFS_EMBDIR_SECTOR_SPACEFORNAMES) { tmp->preventryLen = dirent->entryLen; } } else { tmp = (embdirent_t *) ((char *)dirent - dirent->preventryLen); extraspace = dirent->entryLen - embdirentsize(dirent); if (extraspace < param2) { printf ("sys_fsupdate_directory (%d): not enough extra space (%d < %d)\n", action, extraspace, param2); return (-E_INVAL); } tmp->entryLen += param2; dirent->preventryLen = tmp->entryLen; dirent->entryLen -= param2; bcopy ((char *)dirent, ((char *)dirent + param2), embdirentsize(dirent)); dirent = (embdirent_t *) ((char *)dirent + tmp->entryLen); tmp = (embdirent_t *) ((char *)dirent + dirent->entryLen); if ((u_int) tmp % CFFS_EMBDIR_SECTOR_SPACEFORNAMES) { tmp->preventryLen = dirent->entryLen; } } #endif /*************************** CFFS_DIRECTORY_SETINODENUM ***********************/ } else if (action == CFFS_DIRECTORY_SETINODENUM) { u_int inodeNum = param2; /* no restrictions is directory entry is not currently live */ if (dirent->type == 0) { dirent->inodeNum = inodeNum; /* this case causes a change in which inode a dirent points to. */ /* because the dirent is live, it must always point to a live inode. */ } else { dinode_t *olddinode; dinode_t *newdinode; if (inodeNum == 0) { printf ("sys_fsupdate_directory (%d): can't set inodeNum to 0 is live dirent\n", action); return (-E_INVAL); } if ((param3 == 0) || (param4 == 0)) { printf ("sys_fsupdate_directory (%d): passed NULL for a cheat variable (%d, %d)\n", action, param3, param4); return (-E_INVAL); } /* it is a bug in FS code to have valid dirent with 0 for an inodeNum */ assert (dirent->inodeNum != 0); /* this should be retrievable from dirent->inodeNum, which identifies */ /* the containing disk block -- which must be a directory block. */ olddinode = make_dinode (param3); if (olddinode->dinodeNum != dirent->inodeNum) { printf ("sys_fsupdate_directory (%d): mismatching old inodeNums (%d != %d)\n", action, olddinode->dinodeNum, dirent->inodeNum); return (-E_INVAL); } /* this should be retrievable from inodeNum, which identifies the */ /* containing disk block -- which must be a directory block. */ newdinode = make_dinode (param4); if (newdinode->dinodeNum != inodeNum) { printf ("sys_fsupdate_directory (%d): mismatching old inodeNums (%d != %d)\n", action, newdinode->dinodeNum, inodeNum); return (-E_INVAL); } if ((olddinode->type != newdinode->type) && ((cffs_dinode_isDir(olddinode)) || (cffs_dinode_isDir(newdinode)))) { printf ("sys_fsupdate_directory (%d): can't interchange a directory and non-directory (%x != %x)\n", action, olddinode->type, newdinode->type); return (-E_INVAL); } /* this rule helps get us around an ordering requirement!! */ /* (i.e., if we handle ordering in some other way, it can */ /* go away... */ if (newdinode->linkCount < (cffs_dinode_isDir(newdinode) ? 2 : 1)) { printf ("sys_fsupdate_directory (%d): can't link up to disowned inode\n", action); return (-E_INVAL); } /* the big stickler here is that we need to be able to check whether */ /* the caller has the ability to look at newdinode. In particular, */ /* this requires execute permission on a directory that already */ /* contains an existing link. */ if ((olddinode) && (cffs_dinode_isDir(olddinode))) { /* must check that directory is actually empty... */ } /* GROK - I'm pretty sure I've designed away all need for ordering here */ olddinode->linkCount--; newdinode->linkCount++; dirent->inodeNum = inodeNum; dirent->type = newdinode->type >> 8; } /**************************** CFFS_DIRECTORY_SETTYPE **************************/ } else if (action == CFFS_DIRECTORY_SETTYPE) {
static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, unsigned int mode, dev_t dev, const char *symname, unsigned int size) { const struct qstr *name = &dentry->d_name; struct gfs2_holder ghs[2]; struct inode *inode = NULL; struct gfs2_inode *dip = GFS2_I(dir); struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 }; int error; u64 generation; struct buffer_head *bh = NULL; if (!name->len || name->len > GFS2_FNAMESIZE) return -ENAMETOOLONG; error = gfs2_glock_nq_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); if (error) goto fail; error = create_ok(dip, name, mode); if (error) goto fail_gunlock; error = alloc_dinode(dip, &inum.no_addr, &generation); if (error) goto fail_gunlock; inum.no_formal_ino = generation; error = gfs2_glock_nq_num(sdp, inum.no_addr, &gfs2_inode_glops, LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1); if (error) goto fail_gunlock; error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev, symname, size, &bh); if (error) goto fail_gunlock2; inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, inum.no_formal_ino, 0); if (IS_ERR(inode)) goto fail_gunlock2; error = gfs2_inode_refresh(GFS2_I(inode)); if (error) goto fail_gunlock2; error = gfs2_acl_create(dip, inode); if (error) goto fail_gunlock2; error = gfs2_security_init(dip, GFS2_I(inode), name); if (error) goto fail_gunlock2; error = link_dinode(dip, name, GFS2_I(inode)); if (error) goto fail_gunlock2; if (bh) brelse(bh); gfs2_trans_end(sdp); if (dip->i_alloc->al_rgd) gfs2_inplace_release(dip); gfs2_quota_unlock(dip); gfs2_alloc_put(dip); gfs2_glock_dq_uninit_m(2, ghs); mark_inode_dirty(inode); d_instantiate(dentry, inode); return 0; fail_gunlock2: gfs2_glock_dq_uninit(ghs + 1); if (inode && !IS_ERR(inode)) iput(inode); fail_gunlock: gfs2_glock_dq_uninit(ghs); fail: if (bh) brelse(bh); return error; } /** * gfs2_create - Create a file * @dir: The directory in which to create the file * @dentry: The dentry of the new file * @mode: The mode of the new file * * Returns: errno */ static int gfs2_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { struct inode *inode; int ret; for (;;) { ret = gfs2_create_inode(dir, dentry, S_IFREG | mode, 0, NULL, 0); if (ret != -EEXIST || (nd && (nd->flags & LOOKUP_EXCL))) return ret; inode = gfs2_lookupi(dir, &dentry->d_name, 0); if (inode) { if (!IS_ERR(inode)) break; return PTR_ERR(inode); } } d_instantiate(dentry, inode); return 0; } /** * gfs2_lookup - Look up a filename in a directory and return its inode * @dir: The directory inode * @dentry: The dentry of the new inode * @nd: passed from Linux VFS, ignored by us * * Called by the VFS layer. Lock dir and call gfs2_lookupi() * * Returns: errno */ static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct inode *inode = gfs2_lookupi(dir, &dentry->d_name, 0); if (inode && !IS_ERR(inode)) { struct gfs2_glock *gl = GFS2_I(inode)->i_gl; struct gfs2_holder gh; int error; error = gfs2_glock_nq_init(gl, LM_ST_SHARED, LM_FLAG_ANY, &gh); if (error) { iput(inode); return ERR_PTR(error); } gfs2_glock_dq_uninit(&gh); } return d_splice_alias(inode, dentry); } /** * gfs2_link - Link to a file * @old_dentry: The inode to link * @dir: Add link to this directory * @dentry: The name of the link * * Link the inode in "old_dentry" into the directory "dir" with the * name in "dentry". * * Returns: errno */ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct gfs2_inode *dip = GFS2_I(dir); struct gfs2_sbd *sdp = GFS2_SB(dir); struct inode *inode = old_dentry->d_inode; struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_holder ghs[2]; struct buffer_head *dibh; int alloc_required; int error; if (S_ISDIR(inode->i_mode)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); error = gfs2_glock_nq(ghs); /* parent */ if (error) goto out_parent; error = gfs2_glock_nq(ghs + 1); /* child */ if (error) goto out_child; error = -ENOENT; if (inode->i_nlink == 0) goto out_gunlock; error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC); if (error) goto out_gunlock; error = gfs2_dir_check(dir, &dentry->d_name, NULL); switch (error) { case -ENOENT: break; case 0: error = -EEXIST; default: goto out_gunlock; } error = -EINVAL; if (!dip->i_inode.i_nlink) goto out_gunlock; error = -EFBIG; if (dip->i_entries == (u32)-1) goto out_gunlock; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out_gunlock; error = -EINVAL; if (!ip->i_inode.i_nlink) goto out_gunlock; error = -EMLINK; if (ip->i_inode.i_nlink == (u32)-1) goto out_gunlock; alloc_required = error = gfs2_diradd_alloc_required(dir, &dentry->d_name); if (error < 0) goto out_gunlock; error = 0; if (alloc_required) { struct gfs2_alloc *al = gfs2_alloc_get(dip); if (!al) { error = -ENOMEM; goto out_gunlock; } error = gfs2_quota_lock_check(dip); if (error) goto out_alloc; al->al_requested = sdp->sd_max_dirres; error = gfs2_inplace_reserve(dip); if (error) goto out_gunlock_q; error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + gfs2_rg_blocks(al) + 2 * RES_DINODE + RES_STATFS + RES_QUOTA, 0); if (error) goto out_ipres; } else { error = gfs2_trans_begin(sdp, 2 * RES_DINODE + RES_LEAF, 0); if (error) goto out_ipres; } error = gfs2_meta_inode_buffer(ip, &dibh); if (error) goto out_end_trans; error = gfs2_dir_add(dir, &dentry->d_name, ip); if (error) goto out_brelse; gfs2_trans_add_bh(ip->i_gl, dibh, 1); inc_nlink(&ip->i_inode); ip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(ip, dibh->b_data); mark_inode_dirty(&ip->i_inode); out_brelse: brelse(dibh); out_end_trans: gfs2_trans_end(sdp); out_ipres: if (alloc_required) gfs2_inplace_release(dip); out_gunlock_q: if (alloc_required) gfs2_quota_unlock(dip); out_alloc: if (alloc_required) gfs2_alloc_put(dip); out_gunlock: gfs2_glock_dq(ghs + 1); out_child: gfs2_glock_dq(ghs); out_parent: gfs2_holder_uninit(ghs); gfs2_holder_uninit(ghs + 1); if (!error) { ihold(inode); d_instantiate(dentry, inode); mark_inode_dirty(inode); } return error; } /* * gfs2_unlink_ok - check to see that a inode is still in a directory * @dip: the directory * @name: the name of the file * @ip: the inode * * Assumes that the lock on (at least) @dip is held. * * Returns: 0 if the parent/child relationship is correct, errno if it isn't */ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, const struct gfs2_inode *ip) { int error; if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif if ((dip->i_inode.i_mode & S_ISVTX) && dip->i_inode.i_uid != current_fsuid() && ip->i_inode.i_uid != current_fsuid() && !capable(CAP_FOWNER)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif if (IS_APPEND(&dip->i_inode)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC); if (error) return error; error = gfs2_dir_check(&dip->i_inode, name, ip); if (error) return error; return 0; }