Revert "autorid: reserve 500 IDs at the top of the ALLOC range."
[obnox/samba/samba-obnox.git] / source3 / winbindd / idmap_autorid.c
index af0288bac7f60dab9f21db1e4d52d770d0b079a0..9e930b1748b90a2a9f5b8ce013ade4771746472a 100644 (file)
@@ -338,15 +338,118 @@ static NTSTATUS idmap_autorid_unixids_to_sids(struct idmap_domain *dom,
        return ret;
 }
 
+static bool idmap_autorid_sid_is_special(struct dom_sid *sid)
+{
+       bool match;
+
+       match = sid_check_is_in_wellknown_domain(sid);
+       if (match) {
+               return true;
+       }
+
+       return false;
+}
+
+static NTSTATUS idmap_autorid_sid_to_id_special(struct idmap_domain *dom,
+                                               struct id_map *map)
+{
+       struct idmap_tdb_common_context *common =
+               talloc_get_type_abort(dom->private_data,
+                                     struct idmap_tdb_common_context);
+       uint32_t count;
+       struct autorid_range_config range;
+       NTSTATUS status;
+       uint32_t free_id;
+
+       status = idmap_autorid_get_alloc_range(dom, &range);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* Take the next free ID, counting from the top */
+       free_id = 0;
+       for (count = 0; count < IDMAP_AUTORID_ALLOC_RESERVED; count++) {
+               struct id_map test_map;
+               struct dom_sid sid;
+
+               test_map.sid = &sid;
+               test_map.xid.type = map->xid.type;
+               test_map.xid.id = range.high_id - count;
+               test_map.status = ID_UNKNOWN;
+
+               status = idmap_tdb_common_unixid_to_sid(dom, &test_map);
+               if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, status)) {
+                       free_id = test_map.xid.id;
+                       break;
+               }
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       /* error - get out */
+                       return status;
+               }
+
+               /* mapping exists - try next ID */
+       }
+
+       if (free_id == 0) {
+               return NT_STATUS_NONE_MAPPED;
+       }
+
+       map->status = ID_MAPPED;
+       map->xid.id = free_id;
+
+       status = common->rw_ops->set_mapping(dom, map);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(2, ("Error storing new mapping: %s\n",
+                         nt_errstr(status)));
+               return status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+struct idmap_autorid_sid_to_id_alloc_ctx {
+       struct idmap_domain *dom;
+       struct id_map *map;
+};
+
+static NTSTATUS idmap_autorid_sid_to_id_alloc_action(
+                               struct db_context *db,
+                               void *private_data)
+{
+       struct idmap_autorid_sid_to_id_alloc_ctx *ctx;
+
+       ctx = (struct idmap_autorid_sid_to_id_alloc_ctx *)private_data;
+
+       if (idmap_autorid_sid_is_special(ctx->map->sid)) {
+               NTSTATUS ret;
+
+               ret = idmap_autorid_sid_to_id_special(ctx->dom, ctx->map);
+               if (NT_STATUS_IS_OK(ret)) {
+                       return NT_STATUS_OK;
+               }
+               if (!NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, ret)) {
+                       return ret;
+               }
+
+               DEBUG(10, ("Sepecial sid %s not mapped. falling back to "
+                          "regular allocation\n",
+                          sid_string_dbg(ctx->map->sid)));
+       }
+
+       return idmap_tdb_common_new_mapping(ctx->dom, ctx->map);
+}
+
 /*
  * map a SID to xid using the idmap_tdb like pool
  */
-static NTSTATUS idmap_autorid_sid_to_id_alloc(struct idmap_domain *dom,
-                                       struct id_map *map,
-                                       struct idmap_tdb_common_context *ctx)
+static NTSTATUS idmap_autorid_sid_to_id_alloc(
+                                       struct idmap_tdb_common_context *ctx,
+                                       struct idmap_domain *dom,
+                                       struct id_map *map)
 {
        NTSTATUS ret;
-       int res;
+       struct idmap_autorid_sid_to_id_alloc_ctx alloc_ctx;
 
        map->status = ID_UNKNOWN;
 
@@ -375,31 +478,31 @@ static NTSTATUS idmap_autorid_sid_to_id_alloc(struct idmap_domain *dom,
        DEBUG(10, ("Creating new mapping in pool for %s\n",
                   sid_string_dbg(map->sid)));
 
-       /* create new mapping */
-       res = dbwrap_transaction_start(ctx->db);
-       if (res != 0) {
-               DEBUG(2, ("transaction_start failed\n"));
-               return NT_STATUS_INTERNAL_DB_CORRUPTION;
-       }
+       alloc_ctx.dom = dom;
+       alloc_ctx.map = map;
 
-       ret = idmap_tdb_common_new_mapping(dom, map);
+       ret = dbwrap_trans_do(ctx->db, idmap_autorid_sid_to_id_alloc_action,
+                             &alloc_ctx);
        if (!NT_STATUS_IS_OK(ret)) {
-               if (dbwrap_transaction_cancel(ctx->db) != 0) {
-                       smb_panic("Cancelling transaction failed");
-               }
-               map->status = ID_UNMAPPED;
-               return ret;
+               DEBUG(1, ("Failed to create a new mapping in alloc range: %s\n",
+                         nt_errstr(ret)));
+               return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
 
-       res = dbwrap_transaction_commit(ctx->db);
-       if (res == 0) {
-               map->status = ID_MAPPED;
-               return NT_STATUS_OK;
-       }
+       map->status = ID_MAPPED;
+       return NT_STATUS_OK;
+}
+
+static bool idmap_autorid_domsid_is_for_alloc(struct dom_sid *sid)
+{
+       bool match;
 
-       DEBUG(2, ("transaction_commit failed\n"));
-       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+       match = sid_check_is_wellknown_domain(sid, NULL);
+       if (match) {
+               return true;
+       }
 
+       return false;
 }
 
 static NTSTATUS idmap_autorid_sid_to_id(struct idmap_tdb_common_context *common,
@@ -429,11 +532,11 @@ static NTSTATUS idmap_autorid_sid_to_id(struct idmap_tdb_common_context *common,
                return NT_STATUS_NONE_MAPPED;
        }
 
-       if (sid_check_is_wellknown_domain(&domainsid, NULL)) {
-               DEBUG(10, ("SID %s is well-known, using pool\n",
+       if (idmap_autorid_domsid_is_for_alloc(&domainsid)) {
+               DEBUG(10, ("SID %s is for ALLOC range.\n",
                           sid_string_dbg(map->sid)));
 
-               return idmap_autorid_sid_to_id_alloc(dom, map, common);
+               return idmap_autorid_sid_to_id_alloc(common, dom, map);
        }
 
        if (dom_sid_equal(&domainsid, &global_sid_Builtin) && ignore_builtin) {