#define XATTR_SMACK_IPIN "SMACK64IPIN"
 #define XATTR_SMACK_IPOUT "SMACK64IPOUT"
 #define XATTR_SMACK_EXEC "SMACK64EXEC"
+#define XATTR_SMACK_TRANSMUTE "SMACK64TRANSMUTE"
 #define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX
 #define XATTR_NAME_SMACKIPIN   XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN
 #define XATTR_NAME_SMACKIPOUT  XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT
 #define XATTR_NAME_SMACKEXEC   XATTR_SECURITY_PREFIX XATTR_SMACK_EXEC
+#define XATTR_NAME_SMACKTRANSMUTE XATTR_SECURITY_PREFIX XATTR_SMACK_TRANSMUTE
 
 #define XATTR_CAPS_SUFFIX "capability"
 #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
 
 };
 
 #define        SMK_INODE_INSTANT       0x01    /* inode is instantiated */
+#define        SMK_INODE_TRANSMUTE     0x02    /* directory is transmuting */
 
 /*
  * A label access rule.
 #define SMACK_CIPSO_MAXLEVEL            255     /* CIPSO 2.2 standard */
 #define SMACK_CIPSO_MAXCATNUM           239     /* CIPSO 2.2 standard */
 
+/*
+ * Flag for transmute access
+ */
+#define MAY_TRANSMUTE  64
 /*
  * Just to make the common cases easier to deal with
  */
 /*
  * These functions are in smack_access.c
  */
+int smk_access_entry(char *, char *);
 int smk_access(char *, char *, int, struct smk_audit_info *);
 int smk_curacc(char *, u32, struct smk_audit_info *);
 int smack_to_cipso(const char *, struct smack_cipso *);
        catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8);
 }
 
+/*
+ * Is the directory transmuting?
+ */
+static inline int smk_inode_transmutable(const struct inode *isp)
+{
+       struct inode_smack *sip = isp->i_security;
+       return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0;
+}
+
 /*
  * Present a pointer to the smack label in an inode blob.
  */
 }
 
 /*
- * Present a pointer to the smack label in the curren task blob.
+ * Present a pointer to the smack label in the current task blob.
  */
 static inline char *smk_of_current(void)
 {
 
  */
 int log_policy = SMACK_AUDIT_DENIED;
 
+/**
+ * smk_access_entry - look up matching access rule
+ * @subject_label: a pointer to the subject's Smack label
+ * @object_label: a pointer to the object's Smack label
+ *
+ * This function looks up the subject/object pair in the
+ * access rule list and returns pointer to the matching rule if found,
+ * NULL otherwise.
+ *
+ * NOTE:
+ * Even though Smack labels are usually shared on smack_list
+ * labels that come in off the network can't be imported
+ * and added to the list for locking reasons.
+ *
+ * Therefore, it is necessary to check the contents of the labels,
+ * not just the pointer values. Of course, in most cases the labels
+ * will be on the list, so checking the pointers may be a worthwhile
+ * optimization.
+ */
+int smk_access_entry(char *subject_label, char *object_label)
+{
+       u32 may = MAY_NOT;
+       struct smack_rule *srp;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(srp, &smack_rule_list, list) {
+               if (srp->smk_subject == subject_label ||
+                   strcmp(srp->smk_subject, subject_label) == 0) {
+                       if (srp->smk_object == object_label ||
+                           strcmp(srp->smk_object, object_label) == 0) {
+                               may = srp->smk_access;
+                               break;
+                       }
+               }
+       }
+       rcu_read_unlock();
+
+       return may;
+}
+
 /**
  * smk_access - determine if a subject has a specific access to an object
  * @subject_label: a pointer to the subject's Smack label
               struct smk_audit_info *a)
 {
        u32 may = MAY_NOT;
-       struct smack_rule *srp;
        int rc = 0;
 
        /*
         * access (e.g. read is included in readwrite) it's
         * good.
         */
-       rcu_read_lock();
-       list_for_each_entry_rcu(srp, &smack_rule_list, list) {
-               if (srp->smk_subject == subject_label ||
-                   strcmp(srp->smk_subject, subject_label) == 0) {
-                       if (srp->smk_object == object_label ||
-                           strcmp(srp->smk_object, object_label) == 0) {
-                               may = srp->smk_access;
-                               break;
-                       }
-               }
-       }
-       rcu_read_unlock();
+       may = smk_access_entry(subject_label, object_label);
        /*
         * This is a bit map operation.
         */
 
  *
  *  This file contains the smack hook function implementations.
  *
- *  Author:
+ *  Authors:
  *     Casey Schaufler <casey@schaufler-ca.com>
+ *     Jarkko Sakkinen <ext-jarkko.2.sakkinen@nokia.com>
  *
  *  Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
  *  Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
  *                Paul Moore <paul.moore@hp.com>
+ *  Copyright (C) 2010 Nokia Corporation
  *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License version 2,
 
 #define task_security(task)    (task_cred_xxx((task), security))
 
+#define TRANS_TRUE     "TRUE"
+#define TRANS_TRUE_SIZE        4
+
 /**
  * smk_fetch - Fetch the smack label from a file.
  * @ip: a pointer to the inode
                                     char **name, void **value, size_t *len)
 {
        char *isp = smk_of_inode(inode);
+       char *dsp = smk_of_inode(dir);
+       u32 may;
 
        if (name) {
                *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL);
        }
 
        if (value) {
+               may = smk_access_entry(smk_of_current(), dsp);
+
+               /*
+                * If the access rule allows transmutation and
+                * the directory requests transmutation then
+                * by all means transmute.
+                */
+               if (((may & MAY_TRANSMUTE) != 0) && smk_inode_transmutable(dir))
+                       isp = dsp;
+
                *value = kstrdup(isp, GFP_KERNEL);
                if (*value == NULL)
                        return -ENOMEM;
                if (size == 0 || size >= SMK_LABELLEN ||
                    smk_import(value, size) == NULL)
                        rc = -EINVAL;
+       } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
+               if (!capable(CAP_MAC_ADMIN))
+                       rc = -EPERM;
+               if (size != TRANS_TRUE_SIZE ||
+                   strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
+                       rc = -EINVAL;
        } else
                rc = cap_inode_setxattr(dentry, name, value, size, flags);
 
 static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
                                      const void *value, size_t size, int flags)
 {
-       struct inode_smack *isp;
        char *nsp;
-
-       /*
-        * Not SMACK or SMACKEXEC
-        */
-       if (strcmp(name, XATTR_NAME_SMACK) &&
-           strcmp(name, XATTR_NAME_SMACKEXEC))
-               return;
-
-       isp = dentry->d_inode->i_security;
-
-       /*
-        * No locking is done here. This is a pointer
-        * assignment.
-        */
-       nsp = smk_import(value, size);
+       struct inode_smack *isp = dentry->d_inode->i_security;
 
        if (strcmp(name, XATTR_NAME_SMACK) == 0) {
+               nsp = smk_import(value, size);
                if (nsp != NULL)
                        isp->smk_inode = nsp;
                else
                        isp->smk_inode = smack_known_invalid.smk_known;
-       } else {
+       } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
+               nsp = smk_import(value, size);
                if (nsp != NULL)
                        isp->smk_task = nsp;
                else
                        isp->smk_task = smack_known_invalid.smk_known;
-       }
+       } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
+               isp->smk_flags |= SMK_INODE_TRANSMUTE;
 
        return;
 }
        if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
            strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
            strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
-           strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
+           strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
+           strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
                if (!capable(CAP_MAC_ADMIN))
                        rc = -EPERM;
        } else
        char *csp = smk_of_current();
        char *fetched;
        char *final;
+       char trattr[TRANS_TRUE_SIZE];
+       int transflag = 0;
        struct dentry *dp;
 
        if (inode == NULL)
                 */
                dp = dget(opt_dentry);
                fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp);
-               if (fetched != NULL)
+               if (fetched != NULL) {
                        final = fetched;
-               isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode,
-                                         dp);
+                       if (S_ISDIR(inode->i_mode)) {
+                               trattr[0] = '\0';
+                               inode->i_op->getxattr(dp,
+                                       XATTR_NAME_SMACKTRANSMUTE,
+                                       trattr, TRANS_TRUE_SIZE);
+                               if (strncmp(trattr, TRANS_TRUE,
+                                           TRANS_TRUE_SIZE) == 0)
+                                       transflag = SMK_INODE_TRANSMUTE;
+                       }
+               }
+               isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
 
                dput(dp);
                break;
        else
                isp->smk_inode = final;
 
-       isp->smk_flags |= SMK_INODE_INSTANT;
+       isp->smk_flags |= (SMK_INODE_INSTANT | transflag);
 
 unlockandout:
        mutex_unlock(&isp->smk_lock);
                             void *value, size_t size)
 {
        struct task_smack *tsp;
+       struct task_smack *oldtsp;
        struct cred *new;
        char *newsmack;
 
        if (newsmack == smack_known_web.smk_known)
                return -EPERM;
 
+       oldtsp = p->cred->security;
        new = prepare_creds();
        if (new == NULL)
                return -ENOMEM;
                return -ENOMEM;
        }
        tsp->smk_task = newsmack;
+       tsp->smk_forked = oldtsp->smk_forked;
        new->security = tsp;
        commit_creds(new);
        return size;
 
  * SMK_ACCESSLEN: Maximum length for a rule access field
  * SMK_LOADLEN: Smack rule length
  */
-#define SMK_ACCESS    "rwxa"
-#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
-#define SMK_LOADLEN   (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
+#define SMK_OACCESS    "rwxa"
+#define SMK_ACCESS     "rwxat"
+#define SMK_OACCESSLEN (sizeof(SMK_OACCESS) - 1)
+#define SMK_ACCESSLEN  (sizeof(SMK_ACCESS) - 1)
+#define SMK_OLOADLEN   (SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN)
+#define SMK_LOADLEN    (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
 
 /**
  * smk_netlabel_audit_set - fill a netlbl_audit struct
                seq_putc(s, 'x');
        if (srp->smk_access & MAY_APPEND)
                seq_putc(s, 'a');
+       if (srp->smk_access & MAY_TRANSMUTE)
+               seq_putc(s, 't');
        if (srp->smk_access == 0)
                seq_putc(s, '-');
 
        if (!capable(CAP_MAC_ADMIN))
                return -EPERM;
 
-       if (*ppos != 0 || count != SMK_LOADLEN)
+       if (*ppos != 0)
+               return -EINVAL;
+       /*
+        * Minor hack for backward compatability
+        */
+       if (count < (SMK_OLOADLEN) || count > SMK_LOADLEN)
                return -EINVAL;
 
-       data = kzalloc(count, GFP_KERNEL);
+       data = kzalloc(SMK_LOADLEN, GFP_KERNEL);
        if (data == NULL)
                return -ENOMEM;
 
                goto out;
        }
 
+       /*
+        * More on the minor hack for backward compatability
+        */
+       if (count == (SMK_OLOADLEN))
+               data[SMK_OLOADLEN] = '-';
+
        rule = kzalloc(sizeof(*rule), GFP_KERNEL);
        if (rule == NULL) {
                rc = -ENOMEM;
                goto out_free_rule;
        }
 
+       switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
+       case '-':
+               break;
+       case 't':
+       case 'T':
+               rule->smk_access |= MAY_TRANSMUTE;
+               break;
+       default:
+               goto out_free_rule;
+       }
+
        rc = smk_set_access(rule);
 
        if (!rc)